From 4bee406bd5818da9bab44a3091a618bde34a63c5 Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Sun, 17 Apr 2022 17:53:55 +0200 Subject: [PATCH] add support of clap derive feature #15 Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- Cargo.toml | 1 - README.md | 3 ++- config-derive/Cargo.toml | 2 +- config-derive/src/lib.rs | 13 ++++++------ twelf/Cargo.toml | 6 +++--- twelf/examples/clap.rs | 2 +- twelf/examples/clap_derive.rs | 40 +++++++++++++++++++++++++++++++++++ 7 files changed, 54 insertions(+), 13 deletions(-) create mode 100644 twelf/examples/clap_derive.rs diff --git a/Cargo.toml b/Cargo.toml index b714016..42ad56c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,2 @@ [workspace] members = ["twelf", "config-derive"] - diff --git a/README.md b/README.md index 0b56178..5ad0857 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,7 @@ struct Conf { // Will generate global arguments for each of your fields inside your configuration struct let app = clap::Command::new("test").args(&Conf::clap_args()); +// If you're looking for how to use with clap derive feature, check in the examples directory (twelf/examples/clap_derive.rs) // Init configuration with layers, each layers override only existing fields let config = Conf::with_layers(&[ @@ -67,7 +68,7 @@ Check [here](./twelf/examples) for more examples. Twelf supports crate features, if you only want support for `json`, `env` and `toml` then you just have to add this to your `Cargo.toml` ```toml -twelf = { version = "0.3", default-features = false, features = ["json", "toml", "env"] } +twelf = { version = "0.4", default-features = false, features = ["json", "toml", "env"] } ``` Default features are `["env", "dhall", "clap", "ini", "json", "yaml", "toml"]` diff --git a/config-derive/Cargo.toml b/config-derive/Cargo.toml index 33dd36f..2ba2370 100644 --- a/config-derive/Cargo.toml +++ b/config-derive/Cargo.toml @@ -9,7 +9,7 @@ name = "config-derive" readme = "README.md" repository = "https://github.com/bnjjj/twelf" - version = "0.3.1" + version = "0.4.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/config-derive/src/lib.rs b/config-derive/src/lib.rs index 674be2f..be3e878 100644 --- a/config-derive/src/lib.rs +++ b/config-derive/src/lib.rs @@ -131,7 +131,7 @@ pub fn config(_attrs: TokenStream, item: TokenStream) -> TokenStream { use std::iter::FromIterator; let mut res: std::collections::HashMap = std::collections::HashMap::new(); for layer in layers { - let extension = Self::parse(layer)?; + let extension = Self::parse_twelf(layer)?; res.extend( extension .as_object() @@ -146,11 +146,11 @@ pub fn config(_attrs: TokenStream, item: TokenStream) -> TokenStream { ::twelf::reexports::log::debug!(target: "twelf", "{}={}", key, val); } - ::twelf::reexports::serde_json::from_value(::twelf::reexports::serde_json::Value::Object(::twelf::reexports::serde_json::Map::from_iter(res.into_iter()))).map_err(|e| ::twelf::Error::Deserialize(e.to_string())) + ::twelf::reexports::serde_json::from_value(::twelf::reexports::serde_json::Value::Object(::twelf::reexports::serde_json::Map::from_iter(res.into_iter()))).map_err(|e| ::twelf::Error::Deserialize(format!("config error: {}", e.to_string()))) } #clap_method - fn parse(priority: &::twelf::Layer) -> Result<::twelf::reexports::serde_json::Value, ::twelf::Error> + fn parse_twelf(priority: &::twelf::Layer) -> Result<::twelf::reexports::serde_json::Value, ::twelf::Error> { #[derive(::twelf::reexports::serde::Deserialize, ::twelf::reexports::serde::Serialize)] #[serde(crate = "::twelf::reexports::serde")] @@ -187,15 +187,16 @@ fn build_clap_branch( .iter() .map(|field_name| field_name.to_kebab_case()); + let field_names_clap_cloned = field_names_clap.clone(); #[cfg(feature = "clap")] let clap_branch = quote! { ::twelf::Layer::Clap(matches) => { let mut map: std::collections::HashMap = std::collections::HashMap::new(); #( - if let Some(vmatch) = matches.value_of(#fields_name) { + if let Some(vmatch) = matches.value_of(#field_names_clap_cloned) { map.insert(String::from(#fields_name), vmatch.to_string()); } else if #fields_is_boolean { - if matches.is_present(#fields_name) { + if matches.is_present(#field_names_clap_cloned) { map.insert(String::from(#fields_name), String::from("true")); } } @@ -209,7 +210,7 @@ fn build_clap_branch( #[cfg(feature = "clap")] let clap_method = quote! { pub fn clap_args() -> Vec<::twelf::reexports::clap::Arg<'static>> { vec![#( - ::twelf::reexports::clap::Arg::new(#fields_name).long(#field_names_clap).help(#docs).takes_value(!#fields_is_boolean).global(true) + ::twelf::reexports::clap::Arg::new(#field_names_clap).long(#field_names_clap).help(#docs).takes_value(!#fields_is_boolean).global(true) ),*] }}; #[cfg(not(feature = "clap"))] diff --git a/twelf/Cargo.toml b/twelf/Cargo.toml index 8c13e96..9ce7d9c 100644 --- a/twelf/Cargo.toml +++ b/twelf/Cargo.toml @@ -9,11 +9,11 @@ name = "twelf" readme = "../README.md" repository = "https://github.com/bnjjj/twelf" - version = "0.3.1" + version = "0.4.0" [dependencies] - clap_rs = { version = "3.0", package = "clap", optional = true } - config-derive = { path = "../config-derive", version = "0.3" } + clap_rs = { version = "3.0", package = "clap", optional = true, features = ["derive"] } + config-derive = { path = "../config-derive", version = "0.4" } envy = { version = "0.4.1", git = "https://github.com/bnjjj/envy", branch = "master", optional = true } log = "0.4.14" serde = { version = "1", features = ["derive"] } diff --git a/twelf/examples/clap.rs b/twelf/examples/clap.rs index 63a58fe..a4b66c6 100644 --- a/twelf/examples/clap.rs +++ b/twelf/examples/clap.rs @@ -24,4 +24,4 @@ fn main() { .unwrap(); println!("config - {:?}", config); -} +} \ No newline at end of file diff --git a/twelf/examples/clap_derive.rs b/twelf/examples/clap_derive.rs new file mode 100644 index 0000000..6d2f651 --- /dev/null +++ b/twelf/examples/clap_derive.rs @@ -0,0 +1,40 @@ +#![allow(dead_code)] + +use clap_rs as clap; +use clap::{Parser, Subcommand, CommandFactory}; +use twelf::{config, Layer}; + +#[config] +#[derive(Parser, Debug)] +#[clap(author, version, about, long_about = None)] +struct Config { + #[clap( + long, + help = "Documentation inside clap, to specifiy db_host", + )] + db_host: String, + #[clap( + long, + short, + help = "The number of threads", + )] + #[clap(required = false)] + threads: usize, + #[clap( + long, + short, + help = "Put in verbose mode", + )] + verbose: bool, +} + +// execute `cargo run --example clap_derive -- --help` to display help and documentation +// execute `cargo run --example clap_derive -- --db-host localhost --threads 5` to set configuration +fn main() { + let matches = Config::command().ignore_errors(true).get_matches(); + let config = + Config::with_layers(&[Layer::Env(Some(String::from("APP_"))), Layer::Clap(matches)]) + .unwrap(); + + println!("config - {:?}", config); +}