diff --git a/Cargo.toml b/Cargo.toml index 5558287..fff470f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,3 +49,15 @@ overflow-checks = false [[bench]] name = "bench_apis" harness = false + +[[example]] +name = "clap_mini" +path = "examples/rust/clap_mini.rs" + +[[example]] +name = "clap_layered" +path = "examples/rust/clap_layered.rs" + +[[example]] +name = "clap_full" +path = "examples/rust/clap_full.rs" diff --git a/examples/clap_app.rs b/examples/clap_app.rs deleted file mode 100644 index d44997f..0000000 --- a/examples/clap_app.rs +++ /dev/null @@ -1,33 +0,0 @@ -use clap::Parser; -use hyperparameter::*; - -fn 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` - get param3 = example.param3 or false; // read param `example.param3` or use default value `false` - - 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 - } -} - -#[derive(Parser, Debug)] -#[command(after_long_help=generate_params_help())] -struct DeriveArgs { - /// define hyperparameters - #[arg(short = 'D', long)] - define: Vec, -} - -fn main() { - let args = DeriveArgs::parse(); - with_params! { - params ParamScope::from(&args.define); - - foo() - } -} diff --git a/examples/rust/cfg.toml b/examples/rust/cfg.toml new file mode 100644 index 0000000..4ea687b --- /dev/null +++ b/examples/rust/cfg.toml @@ -0,0 +1,2 @@ +[example] +param1 = "from config" \ No newline at end of file diff --git a/examples/rust/clap_full.rs b/examples/rust/clap_full.rs new file mode 100644 index 0000000..4daccf9 --- /dev/null +++ b/examples/rust/clap_full.rs @@ -0,0 +1,53 @@ +use std::path::Path; + +use clap::Parser; +use config::{self, File}; +use hyperparameter::*; + +/// Defines the command-line arguments for the application. +#[derive(Parser)] +#[command(after_long_help=generate_params_help())] +struct CommandLineArgs { + /// Specifies hyperparameters in the format `-D key=value` via the command line. + #[arg(short = 'D', long)] + define: Vec, + + /// Specifies the configuration file path. + #[arg(short = 'C', long, default_value = "examples/rust/cfg.toml")] + config: String, +} + +fn foo(desc: &str) { + with_params! { + get param1 = example.param1 or "default".to_string(); + + println!("param1={} // {}", param1, desc); + } +} + +fn main() { + let args = CommandLineArgs::parse(); + let config_path = Path::new(&args.config); + let config = config::Config::builder() + .add_source(File::from(config_path)) + .build() + .expect("Failed to load configuration file."); // Improved error handling + + foo("Outside any specific scope"); + + with_params! { // Scope with configuration file parameters + params config.param_scope(); + + foo("Within configuration file scope"); + with_params! { // Scope with command-line arguments + params ParamScope::from(&args.define); + + foo("Within command-line arguments scope"); + with_params! { // User-defined scope + set example.param1= "scoped".to_string(); + + foo("Within user-defined scope"); + } + } + } +} diff --git a/examples/rust/clap_layered.rs b/examples/rust/clap_layered.rs new file mode 100644 index 0000000..403d27a --- /dev/null +++ b/examples/rust/clap_layered.rs @@ -0,0 +1,46 @@ +use std::path::Path; + +use clap::Parser; +use config::{self, File}; +use hyperparameter::*; + +/// Defines the command-line arguments for the application. +#[derive(Parser)] +#[command(after_long_help=generate_params_help())] +struct CommandLineArgs { + /// Specifies hyperparameters in the format `-D key=value` via the command line. + #[arg(short = 'D', long)] + define: Vec, + + /// Specifies the configuration file path. + #[arg(short = 'C', long, default_value = "examples/rust/cfg.toml")] + config: String, +} + +fn main() { + let args = CommandLineArgs::parse(); + let config_path = Path::new(&args.config); + let config = config::Config::builder() + .add_source(File::from(config_path)) + .build() + .expect("Failed to load configuration file."); // Improved error handling + + // No scope + println!("param1={} // Outside any specific scope", get_param!(example.param1, "default".to_string())); + + with_params! { // Scope with configuration file parameters + params config.param_scope(); + + println!("param1={} // Within configuration file scope", get_param!(example.param1, "default".to_string())); + with_params! { // Scope with command-line arguments + params ParamScope::from(&args.define); + + println!("param1={} // Within command-line arguments scope", get_param!(example.param1, "default".to_string(), "Example param1")); + with_params! { // User-defined scope + set example.param1= "scoped".to_string(); + + println!("param1={} // Within user-defined scope", get_param!(example.param1, "default".to_string())); + } + } + } +} diff --git a/examples/rust/clap_mini.rs b/examples/rust/clap_mini.rs new file mode 100644 index 0000000..37b4862 --- /dev/null +++ b/examples/rust/clap_mini.rs @@ -0,0 +1,22 @@ +use clap::Parser; +use hyperparameter::*; + +/// Defines the command-line arguments for the application. +#[derive(Parser)] +#[command(after_long_help=generate_params_help())] +struct CommandLineArgs { + /// Specifies hyperparameters in the format `-D key=value` via the command line. + #[arg(short = 'D', long)] + define: Vec, +} + +fn main() { + let args = CommandLineArgs::parse(); + with_params! { + params ParamScope::from(&args.define); + // Retrieves `example.param1` with a default value of `1` if not specified. + println!("param1={}", get_param!(example.param1, 1)); + // Displays a help message when ` --help` is executed. + println!("param2={}", get_param!(example.param2, false, "Example param2")); + } +} \ No newline at end of file diff --git a/src/api.rs b/src/api.rs index b3dfa47..9573d28 100644 --- a/src/api.rs +++ b/src/api.rs @@ -7,7 +7,7 @@ use crate::storage::{ use crate::value::{Value, EMPTY}; use crate::xxh::XXHashable; -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum ParamScope { Nothing, Just(Tree), @@ -180,9 +180,7 @@ macro_rules! get_param { { const CONST_HELP: &str = $help; #[::linkme::distributed_slice(PARAMS)] - static help: (&str, &str) = ( - CONST_KEY, CONST_HELP - ); + static help: (&str, &str) = (CONST_KEY, CONST_HELP); } THREAD_STORAGE.with(|ts| ts.borrow_mut().get_or_else(CONST_HASH, $default)) }}; @@ -242,6 +240,18 @@ macro_rules! with_params { with_params!(params $ps; $($body)*) }; + ( + params $ps:expr; + params $nested:expr; + + $($body:tt)* + ) => { + $ps.enter(); + let ret = with_params!(params $nested; $($body)*); + $ps.exit(); + ret + }; + ( get $name:ident = $($key:ident).+ or $default:expr; @@ -257,7 +267,6 @@ macro_rules! with_params { $($body:tt)* ) => { - $ps.enter(); let ret = { let $name = get_param!($($key).+, $default); @@ -266,7 +275,6 @@ macro_rules! with_params { }; $ps.exit(); ret - }; ( @@ -279,6 +287,11 @@ macro_rules! with_params { $ps.exit(); ret }}; + + ($($body:tt)*) => {{ + let ret = {$($body)*}; + ret + }}; } #[macro_export] diff --git a/src/cli.rs b/src/cli.rs index c001890..ee3ea1c 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -7,6 +7,9 @@ pub static PARAMS: [(&str, &str)]; pub fn generate_params_help() -> String { let mut params: HashMap> = HashMap::default(); + if PARAMS.len() == 0 { + return "".to_string(); + } for kv in PARAMS { params .entry(kv.0.to_string())