From 1056e723ce63246a0e344312948ddf10b61b365b Mon Sep 17 00:00:00 2001 From: reiase Date: Sat, 16 Mar 2024 23:18:54 +0800 Subject: [PATCH] support clap help message --- Cargo.toml | 13 ++++++------ examples/clap_app.rs | 12 +++++------ src/api.rs | 14 +++++++++++++ src/cli.rs | 48 ++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 6 ++++++ tests/test_cli.rs | 16 +++++++++++++++ 6 files changed, 96 insertions(+), 13 deletions(-) create mode 100644 src/cli.rs create mode 100644 tests/test_cli.rs diff --git a/Cargo.toml b/Cargo.toml index 9f6b0d4..5558287 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "hyperparameter" -version = "0.5.8" +version = "0.5.9" license = "Apache-2.0" -description = " Hyperparameter, Make configurable AI applications.Build for Python hackers. " +description = "A high performance configuration system for Rust and Python." homepage = "https://reiase.github.io/hyperparameter/" readme = "README.md" repository = "https://github.com/reiase/hyperparameter" @@ -16,10 +16,11 @@ exclude = [ ] [features] -default = ["json", "toml", "ini"] +default = ["json", "toml", "ini", "clap"] json = ["config/json"] toml = ["config/toml"] ini = ["config/ini"] +clap = ["dep:linkme", "dep:clap"] [lib] name = "hyperparameter" @@ -33,9 +34,9 @@ signal-hook = "0.3.17" tokio = { version = "1.31.0", features = ["full"] } xxhash-rust = { version = "0.8.7", features = ["xxh3", "xxh64", "const_xxh64"] } const-str = "0.5.6" -nu-ansi-term = "0.49.0" -local-ip-address = "0.5.6" -config = "0.13.3" +config = "0.14.0" +linkme = {version = "0.3", optional = true} +clap = {version = "4.4.7", optional = true} [dev-dependencies] proptest = "1.2.0" diff --git a/examples/clap_app.rs b/examples/clap_app.rs index b20e80f..d44997f 100644 --- a/examples/clap_app.rs +++ b/examples/clap_app.rs @@ -2,7 +2,6 @@ use clap::Parser; use hyperparameter::*; fn foo() { - println!("in function foo"); with_params! { get param1 = example.param1 or 1; // read param `example.param1` or use default value `1` get param2 = example.param2 or String::from("2"); // read param `example.param2` or use default value `2` @@ -11,16 +10,15 @@ fn foo() { println!("param1={}", param1); println!("param2={}", param2); println!("param3={}", param3); + println!("param4={}", get_param!(param4, 1, "help message for param4")); // leave a help msg when reading params + println!("param4={}", get_param!(param4, 1, "another help for param4")); // leave another help msg } } -fn bar() { - println!("in function bar"); - foo() -} - #[derive(Parser, Debug)] +#[command(after_long_help=generate_params_help())] struct DeriveArgs { + /// define hyperparameters #[arg(short = 'D', long)] define: Vec, } @@ -30,6 +28,6 @@ fn main() { with_params! { params ParamScope::from(&args.define); - bar() + foo() } } diff --git a/src/api.rs b/src/api.rs index cd768f6..b3dfa47 100644 --- a/src/api.rs +++ b/src/api.rs @@ -172,6 +172,20 @@ macro_rules! get_param { THREAD_STORAGE.with(|ts| ts.borrow_mut().get_or_else(CONST_HASH, $default)) // ParamScope::default().get_or_else(CONST_HASH, $default) }}; + + ($name:expr, $default:expr, $help: expr) => {{ + const CONST_KEY: &str = const_str::replace!(stringify!($name), ";", ""); + const CONST_HASH: u64 = xxhash_rust::const_xxh64::xxh64(CONST_KEY.as_bytes(), 42); + // ParamScope::default().get_or_else(CONST_HASH, $default) + { + const CONST_HELP: &str = $help; + #[::linkme::distributed_slice(PARAMS)] + static help: (&str, &str) = ( + CONST_KEY, CONST_HELP + ); + } + THREAD_STORAGE.with(|ts| ts.borrow_mut().get_or_else(CONST_HASH, $default)) + }}; } /// Define or use `hyperparameters` in a code block. diff --git a/src/cli.rs b/src/cli.rs new file mode 100644 index 0000000..c001890 --- /dev/null +++ b/src/cli.rs @@ -0,0 +1,48 @@ +use std::collections::{HashMap, HashSet}; + +use clap::builder::Styles; + +#[::linkme::distributed_slice] +pub static PARAMS: [(&str, &str)]; + +pub fn generate_params_help() -> String { + let mut params: HashMap> = HashMap::default(); + for kv in PARAMS { + params + .entry(kv.0.to_string()) + .and_modify(|s| { + s.insert(kv.1.to_string()); + }) + .or_insert(HashSet::from([kv.1.to_string()])); + } + let mut params: Vec<_> = params + .iter() + .map(|kv| { + let mut descs = Vec::from_iter(kv.1.iter().map(|x| x.clone())); + descs.sort(); + (kv.0.clone(), descs.join("\n\t")) + }) + .collect(); + params.sort_by_key(|x| x.0.clone()); + + let styles = Styles::default(); + let header = styles.get_header(); + let literal = styles.get_literal(); + format!( + "{}Hyperparameters:{}\n", + header.render(), + header.render_reset() + ) + ¶ms + .iter() + .map(|kv| { + format!( + " {}{}{}\n\t{}", + literal.render(), + kv.0, + literal.render_reset(), + kv.1 + ) + }) + .collect::>() + .join("\n\n") +} diff --git a/src/lib.rs b/src/lib.rs index 1f6b84a..aa8f821 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,3 +21,9 @@ pub mod api; pub mod cfg; pub mod ffi; pub mod xxh; + +// #[cfg(clap)] +pub mod cli; +// #[cfg(clap)] +pub use crate::cli::PARAMS; +pub use crate::cli::generate_params_help; \ No newline at end of file diff --git a/tests/test_cli.rs b/tests/test_cli.rs new file mode 100644 index 0000000..d6394c3 --- /dev/null +++ b/tests/test_cli.rs @@ -0,0 +1,16 @@ +use hyperparameter::PARAMS; +use linkme::distributed_slice; + +#[test] +fn test_cli() { + #[distributed_slice(PARAMS)] + static param1: (&str, &str) = ( + "key1", "val1" + ); + + assert!(PARAMS.len()==1); + + for kv in PARAMS { + println!("{} => {}", kv.0, kv.1); + } +} \ No newline at end of file