clap-config-fallback 0.1.13

Add config file fallback to clap while preserving parsing, validation and error handling
Documentation
use std::io::Write;

use clap::{Args, Parser};
use clap_config_fallback::{ConfigArgs, ConfigParser};
use eyre::Result;
use tempfile::NamedTempFile;

#[derive(Debug, Parser, ConfigParser, PartialEq, Eq)]
struct Cli {
    #[command(flatten)]
    empty: EmptyArgs,
    #[command(flatten)]
    logging: LoggingArgs,
    #[arg(long)]
    profile: String,
    #[arg(long)]
    #[config(path, format = "toml")]
    config_path: Option<String>,
}

#[derive(Debug, Args, ConfigArgs, PartialEq, Eq)]
struct LoggingArgs {
    #[arg(long)]
    level: String,
    #[arg(long)]
    interval_secs: u16,
}

#[derive(Debug, Args, ConfigArgs, PartialEq, Eq)]
struct EmptyArgs {}

#[test]
fn nested_fields_can_be_loaded_from_config() -> Result<()> {
    let mut file = NamedTempFile::new()?;

    writeln!(file, r#"profile = "prod""#)?;
    writeln!(file, r#"level = "warn""#)?;
    writeln!(file, "interval_secs = 30")?;

    let cli = Cli::try_parse_with_config_from([
        "bin",
        "--config-path",
        &file.path().display().to_string(),
    ])?;

    assert_eq!(cli.profile, "prod");
    assert_eq!(cli.logging.level, "warn");
    assert_eq!(cli.logging.interval_secs, 30);

    Ok(())
}

#[test]
fn nested_cli_values_override_nested_config() -> Result<()> {
    let mut file = NamedTempFile::new()?;

    writeln!(file, r#"profile = "prod""#)?;
    writeln!(file, r#"level = "warn""#)?;
    writeln!(file, "interval_secs = 30")?;

    let cli = Cli::try_parse_with_config_from([
        "bin",
        "--profile",
        "dev",
        "--level",
        "error",
        "--interval-secs",
        "10",
        "--config-path",
        &file.path().display().to_string(),
    ])?;

    assert_eq!(cli.profile, "dev");
    assert_eq!(cli.logging.level, "error");
    assert_eq!(cli.logging.interval_secs, 10);

    Ok(())
}