clap_config_file_core/
lib.rs

1use clap::Parser;
2use serde::{Deserialize, Serialize};
3
4#[derive(Parser, Debug, Default, Deserialize, Serialize)]
5#[command(trailing_var_arg = true)]
6pub struct ConfigFile {
7    /// If true, the config file will not be loaded
8    #[arg(long = "no-config", default_value_t = false)]
9    pub no_config: bool,
10
11    /// Path to the configuration file
12    #[arg(long = "config-file")]
13    pub config_file: Option<std::path::PathBuf>,
14
15    /// Raw configuration string
16    #[arg(long = "config")]
17    pub raw_config: Option<String>,
18
19    /// Leftover arguments
20    #[arg(last = true)]
21    pub leftover: Vec<String>,
22}
23
24impl ConfigFile {
25    pub fn load(default_config: &str) -> Self {
26        let args = if cfg!(test) {
27            if let Ok(args) = std::env::var("ARGS_TEST") {
28                Self::parse_from(args.split_whitespace())
29            } else {
30                Self::parse()
31            }
32        } else {
33            Self::parse()
34        };
35
36        if args.no_config {
37            return args;
38        }
39
40        // Try to load from raw config first
41        if let Some(raw_config) = args.raw_config.as_ref() {
42            if let Ok(config) = serde_yaml::from_str(raw_config) {
43                return config;
44            }
45        }
46
47        // Then try to load from config file
48        if let Some(config_path) = args.config_file.as_ref() {
49            match std::fs::read_to_string(config_path) {
50                Ok(contents) => {
51                    if let Ok(config) = serde_yaml::from_str(&contents) {
52                        return config;
53                    }
54                }
55                Err(e) => eprintln!("Failed to read config file: {}", e),
56            }
57        }
58
59        // Finally try default config
60        if let Ok(config) = serde_yaml::from_str(default_config) {
61            return config;
62        }
63
64        args
65    }
66}