# Clap and Serde derive [![Crates.io](https://img.shields.io/crates/v/clap-serde-derive?style=flat-square&logo=rust)](https://crates.io/crates/clap-serde-derive) [![Crates.io](https://img.shields.io/crates/d/clap-serde-derive?style=flat-square&logo=rust)](https://crates.io/crates/clap-serde-derive) [![Docs.rs](https://img.shields.io/badge/docs.rs-clap--serde--derive-1234?style=flat-square&logo=docs.rs)](https://docs.rs/clap-serde-derive) [![License](https://img.shields.io/gitlab/license/DPDmancul/clap-serde-derive?color=brightgreen&style=flat-square&logo=gnu)](https://gitlab.com/DPDmancul/clap-serde-derive/-/blob/main/LICENSE.md) [![GitLab pipeline](https://img.shields.io/gitlab/pipeline/DPDmancul/clap-serde-derive/main?label=tests&style=flat-square&logo=gitlab)](https://gitlab.com/DPDmancul/clap-serde-derive/builds)
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. ```rust ignore 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. ```rust 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, /// 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::<::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. ```rust 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, /// Config file #[arg(short, long = "config", default_value = "config.yml")] config_path: std::path::PathBuf, /// Rest of arguments #[command(flatten)] pub config: ::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::<_, ::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) }; ```