Skip to content

Navigation Menu

Sign in
Sign up

Repository files navigation

Clap and Serde derive

Crates.io Crates.io Docs.rs License GitLab pipeline

With the ClapSerde procedural macro both clap and serde can be derived from a struct.
Then the struct can be parsed from clap and serde sources as in a layered config: the last source has the precedence.

Args::from(serde_parsed)
 .merge_clap();

In the snippet the precedence is:

  1. Command line from clap;
  2. Config file from serde;
  3. Default values.

Example

In this example we define a struct which derives both clap and serde. The struct has various parameter type and also various attributes on its fields.

Finally we parse the structure from a YAML file with serde and then from command line with clap. The arguments from clap will override those from serde; the default value will be used if no source contained the field.

use clap_serde_derive::{
 clap::{self, ArgAction},
 serde::Serialize,
 ClapSerde,
};
#[derive(ClapSerde, Serialize)]
#[derive(Debug)]
#[command(author, version, about)]
pub struct Args {
 /// Input files
 pub input: Vec<std::path::PathBuf>,
 /// String argument
 #[arg(short, long)]
 name: String,
 /// Skip serde deserialize
 #[default(13)]
 #[serde(skip_deserializing)]
 #[arg(long = "num")]
 pub clap_num: u32,
 /// Skip clap
 #[serde(rename = "number")]
 #[arg(skip)]
 pub serde_num: u32,
 /// Recursive fields
 #[clap_serde]
 #[command(flatten)]
 pub suboptions: SubConfig,
}
#[derive(ClapSerde, Serialize)]
#[derive(Debug)]
pub struct SubConfig {
 #[default(true)]
 #[arg(long = "no-flag", action = ArgAction::SetFalse)]
 pub flag: bool,
}
let args = Args::from(serde_yaml::from_str::<<Args as ClapSerde>::Opt>("number: 12").unwrap())
 .merge_clap();
assert_eq!(
 serde_yaml::to_string(&args).unwrap(),
 serde_yaml::to_string(&Args {
 serde_num: 12,
 clap_num: 13,
 ..Args::default()
 })
 .unwrap(),
);

Config path from command line

You can easily take the config file path from command line in this way.

use std::{fs::File, io::BufReader};
use clap_serde_derive::{
 clap::{self, Parser},
 ClapSerde,
};
#[derive(Parser)]
#[command(author, version, about)]
struct Args {
 /// Input files
 input: Vec<std::path::PathBuf>,
 /// Config file
 #[arg(short, long = "config", default_value = "config.yml")]
 config_path: std::path::PathBuf,
 /// Rest of arguments
 #[command(flatten)]
 pub config: <Config as ClapSerde>::Opt,
}
#[derive(ClapSerde)]
struct Config {
 /// String argument
 #[arg(short, long)]
 name: String,
}
// Parse whole args with clap
let mut args = Args::parse();
// Get config file
let config = if let Ok(f) = File::open(&args.config_path) {
 // Parse config with serde
 match serde_yaml::from_reader::<_, <Config as ClapSerde>::Opt>(BufReader::new(f)) {
 // merge config already parsed from clap
 Ok(config) => Config::from(config).merge(&mut args.config),
 Err(err) => panic!("Error in configuration file:\n{}", err),
 }
} else {
 // If there is not config file return only config parsed from clap
 Config::from(&mut args.config)
};

About

Merge results from clap and serde into struct with derive. Mirror of https://gitlab.com/DPDmancul/clap-serde-derive

Topics

Resources

Stars

7 stars

Watchers

1 watching

Forks

Used by

Contributors

Languages

AltStyle によって変換されたページ (->オリジナル) /