Compare commits

..

3 commits

Author SHA1 Message Date
53bbfd9501 ds files 2025-10-31 22:30:29 +00:00
5631a12c7c feat: add profile option to Args with default 'default'
Co-authored-by: aider (openai/gpt-5) <aider@aider.chat>
2025-10-31 22:28:22 +00:00
c1013feda6 feat: add multi-profile config support with profile selection
Co-authored-by: aider (openai/gpt-5) <aider@aider.chat>
2025-10-31 22:27:49 +00:00
2 changed files with 43 additions and 5 deletions

1
.gitignore vendored
View file

@ -17,3 +17,4 @@ target
# option (not recommended) you can uncomment the following to ignore the entire idea folder. # option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/ #.idea/
Cargo.lock Cargo.lock
.DS_Store

View file

@ -1,12 +1,13 @@
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use clap::Parser; use clap::Parser;
use serde::Deserialize; use serde::Deserialize;
use std::collections::HashMap;
use std::fs; use std::fs;
use std::path::Path; use std::path::Path;
use std::process::Command; use std::process::Command;
use tempfile::TempDir; use tempfile::TempDir;
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize, Clone)]
struct Settings { struct Settings {
nas_host: String, nas_host: String,
nas_user: String, nas_user: String,
@ -14,12 +15,21 @@ struct Settings {
cookies_file: String, cookies_file: String,
} }
#[derive(Debug, Deserialize)]
struct AppConfig {
profiles: HashMap<String, Settings>,
}
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)] #[command(author, version, about, long_about = None)]
struct Args { struct Args {
/// The URL of the video/music to download /// The URL of the video/music to download
#[arg(name = "URL")] #[arg(name = "URL")]
url: String, url: String,
/// Profile name to use from config (falls back to 'default')
#[arg(short, long, default_value = "default")]
profile: String,
} }
fn main() -> Result<()> { fn main() -> Result<()> {
@ -48,10 +58,37 @@ fn main() -> Result<()> {
println!("[INFO] Using config file at: {}", config_path.display()); println!("[INFO] Using config file at: {}", config_path.display());
let settings = config::Config::builder() let cfg = config::Config::builder()
.add_source(config::File::from(config_path)) .add_source(config::File::from(config_path.clone()))
.build()? .build()?;
.try_deserialize::<Settings>()?;
let selected_profile = args.profile.clone();
let settings: Settings = match cfg.clone().try_deserialize::<AppConfig>() {
Ok(app_cfg) => {
if let Some(s) = app_cfg.profiles.get(&selected_profile).cloned() {
println!("[INFO] Using profile: {}", selected_profile);
s
} else if let Some(s) = app_cfg.profiles.get("default").cloned() {
if selected_profile != "default" {
println!("[WARN] Profile '{}' not found. Falling back to 'default'.", selected_profile);
let available = app_cfg.profiles.keys().cloned().collect::<Vec<_>>().join(", ");
println!("[INFO] Available profiles: {}", available);
} else {
println!("[INFO] Using profile: default");
}
s
} else {
anyhow::bail!("No profile '{}' found and no 'default' profile defined in config.", selected_profile);
}
}
Err(_) => {
if selected_profile != "default" {
println!("[WARN] Requested profile '{}' but config file uses single-profile format. Using that single profile.", selected_profile);
}
println!("[INFO] Using legacy single-profile configuration.");
cfg.try_deserialize::<Settings>()?
}
};
// Create temp directory // Create temp directory
let temp_dir = TempDir::new().context("Failed to create temporary directory")?; let temp_dir = TempDir::new().context("Failed to create temporary directory")?;