Как получить ValueSource для аргумента?

Я использую Clap (derive) v4.x для приложения Rust CLI. Основы работают правильно для аргументов командной строки, значений переменных среды и значений по умолчанию.

Затем для каждого совпадения я хочу определить ValueSource (по умолчанию, переменная env, командная строка), но не могу понять, как получить эту информацию с помощью производной подкоманды (т. е. моей ContainerCommand в коде ниже). Я надеюсь, что кто-то может дать мне подсказку или подтолкнуть в правильном направлении.

Мне нужно реализовать иерархическую оценку потенциальных данных конфигурации для приложения: cmdline > envvar > файл конфигурации > defaults.

Моя цель — десериализовать информацию о конфигурации из файла toml. Скопируйте данные из clap в структуру Config. Для любых элементов в структуре Config, которые имеют значение None или взяты из значений по умолчанию, я заменю значение тем, что найдено в данных файла toml, если он существует. В противном случае будет установлено значение по умолчанию.

Я знаю, что существуют отличные пакеты (figment, Config-rs, Twelf,…), которые реализуют иерархическую обработку конфигурации, но каждый из них создает уникальные проблемы/проблемы, когда я пытаюсь использовать их в нашем приложении. Следовательно, я хочу использовать простое решение, которое я описал.

Вот приложение-соломен, которое я использую, чтобы во всем разобраться:

/// This is the entry point for the application.
fn main() -> Result<(), Box<dyn std::error::Error>> {
    let app = App::parse();
    dbg!(&app);
    
    match &app.command {
        Command::Container(c) => {
             //let matches = ContainerCommand::command().get_matches();
    //         if matches.contains_id("name") {
    //             println!("{:?}", c.name);
    //             match matches.value_source("name").unwrap() {
    //                 ValueSource::DefaultValue => { println!("Is the default value")}
    //                 ValueSource::EnvVariable => { println!("Is the envvar value")}
    //                 ValueSource::CommandLine => { println!("Is the commandline value")}
    //                 _ => { println!("Unknown source for the value...")}
    //             }
    //         }
         }
     }

    Ok(())
}

/// The definition of the command line and its arguments
#[derive(Debug, Parser, Serialize)]
#[command(author, version, about, long_about = None, next_line_help = true, propagate_version = true)]
pub struct App {
    #[command(flatten)]
    global_opts: GlobalOpts,

    #[command(subcommand)]
    command: Command,
}

#[derive(Debug, Args, Serialize)]
struct GlobalOpts {
    /// config_path is the path to a configuration (.toml) file, which defaults to the current directory.
    #[arg(short = 'c', long, global = true, default_value = "config.toml")]
    config_path: std::path::PathBuf,
}

#[derive(Debug, Subcommand, Deserialize, Serialize)]
enum Command {
    #[command(about = "The hash container command determines the base64 binary MD5 hash for each blob in a container.", long_about = None)]
    /// Determines the base64 binary MD5 hash for each blob in a container
    Container(ContainerCommand),
}

/// The definition of the command line and its arguments
#[derive(Parser, Debug, Deserialize, Serialize)]
struct ContainerCommand {
    /// The name of a client
    #[arg(short = 'n', long, env("NAME"), default_value = "kdev")]
    name: Option<String>,
}
Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
0
0
62
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы не можете использовать parse, так как он уже выполняет весь анализ и удаляет всю необходимую информацию. Вместо этого вам нужно использовать App::command().get_matches():

    use clap::CommandFactory;
    let matches = App::command().get_matches_from(["self", "-c", "foo", "container"]);
    if let Some((_subcommand_name, subcommand_matches)) = matches.subcommand() {
        match subcommand_matches.value_source("name") {
            Some(ValueSource::DefaultValue) => println!("Is the default value"),
            Some(ValueSource::EnvVariable) => println!("Is the envvar value"),
            Some(ValueSource::CommandLine) => println!("Is the commandline value"),
            Some(_) => println!("Unknown source for the value..."),
            None => (),
        }
    }

и позже вы сможете получить App, используя FromArgMatches:

    use clap::FromArgMatches;
    let app = App::from_arg_matches(&matches).unwrap();

Другие вопросы по теме