1use std::path::{Path, PathBuf};
2
3use directories::ProjectDirs;
4use serde::{Deserialize, Serialize};
5
6use crate::cli::Cli;
7use crate::error::{AppError, Result};
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct FileConfig {
11 pub cache_dir: Option<PathBuf>,
12 pub request_delay_ms: Option<u64>,
13 pub concurrency: Option<usize>,
14 pub timeout_ms: Option<u64>,
15 pub user_agent: Option<String>,
16 pub max_collectors: Option<usize>,
17 pub max_depth: Option<usize>,
18}
19
20#[derive(Debug, Clone)]
21pub struct Settings {
22 pub cache_dir: PathBuf,
23 pub request_delay_ms: u64,
24 pub concurrency: usize,
25 pub timeout_ms: u64,
26 pub user_agent: String,
27}
28
29impl Settings {
30 pub fn load(cli: &Cli) -> Result<Self> {
31 let file_cfg = load_file_config(cli.config.as_deref())?;
32 let project_dirs = ProjectDirs::from("dev", "jdhoffa", "wax").ok_or_else(|| {
33 AppError::InvalidInput("unable to determine cache directory".to_string())
34 })?;
35
36 let cache_dir = cli
37 .cache_dir
38 .clone()
39 .or(file_cfg.cache_dir)
40 .unwrap_or_else(|| project_dirs.cache_dir().to_path_buf());
41
42 Ok(Self {
43 cache_dir,
44 request_delay_ms: cli
45 .rate_limit_ms
46 .or(file_cfg.request_delay_ms)
47 .unwrap_or(750),
48 concurrency: cli.concurrency.or(file_cfg.concurrency).unwrap_or(4),
49 timeout_ms: cli.timeout_ms.or(file_cfg.timeout_ms).unwrap_or(10_000),
50 user_agent: cli
51 .user_agent
52 .clone()
53 .or(file_cfg.user_agent)
54 .unwrap_or_else(|| "wax/0.1".to_string()),
55 })
56 }
57}
58
59fn load_file_config(path: Option<&Path>) -> Result<FileConfig> {
60 let Some(path) = path else {
61 return Ok(FileConfig {
62 cache_dir: None,
63 request_delay_ms: None,
64 concurrency: None,
65 timeout_ms: None,
66 user_agent: None,
67 max_collectors: None,
68 max_depth: None,
69 });
70 };
71
72 let text = std::fs::read_to_string(path)?;
73 let cfg = toml::from_str(&text)?;
74 Ok(cfg)
75}