Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add clap examples #16

Merged
merged 1 commit into from
Mar 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ version = "0.5.9"
license = "Apache-2.0"
description = "A high performance configuration system for Rust and Python."
homepage = "https://reiase.github.io/hyperparameter/"
readme = "README.md"
readme = "examples/rust/README.md"
repository = "https://github.com/reiase/hyperparameter"
authors = ["reiase <[email protected]>"]
edition = "2021"
Expand Down Expand Up @@ -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"
29 changes: 29 additions & 0 deletions benches/bench_apis.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use config::Config;
use criterion::{black_box, criterion_group, criterion_main, Criterion};

use hyperparameter::*;
Expand All @@ -16,6 +17,12 @@ fn foo_with_ps(x: i64) -> i64 {
}
}

#[inline(never)]
fn foo_with_config(x: i64, cfg: &Config) -> i64 {
let y = cfg.get_int("y").unwrap();
x + y
}

#[inline(never)]
fn call_foo(nloop: i64) -> i64 {
let mut sum = 0;
Expand Down Expand Up @@ -66,6 +73,15 @@ fn call_foo_with_ps_and_raw_btree(nloop: i64) -> i64 {
sum
}

#[inline(never)]
fn call_foo_with_config_rs(nloop: i64, cfg: &Config) -> i64 {
let mut sum = 0;
for i in 0..nloop {
sum = sum + foo_with_config(i, cfg);
}
sum
}

pub fn bench_apis(c: &mut Criterion) {
c.bench_function("raw api", |b| b.iter(|| call_foo(black_box(10000))));
}
Expand All @@ -88,6 +104,19 @@ pub fn bench_apis_with_ps(c: &mut Criterion) {
});
}

pub fn bench_config_rs(c: &mut Criterion) {
let cfg = config::Config::builder()
.add_source(config::File::from_str(
"{\"y\": 1}",
config::FileFormat::Json,
))
.build()
.unwrap();
c.bench_function("raw api with config-rs", |b| {
b.iter(|| call_foo_with_config_rs(black_box(10000), &cfg))
});
}

criterion_group!(
benches,
bench_apis,
Expand Down
33 changes: 0 additions & 33 deletions examples/clap_app.rs

This file was deleted.

135 changes: 135 additions & 0 deletions examples/rust/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@

# High-Performance Rust Configuration System

Hyperparameter is a high-performance configuration system designed for Rust and Python, supporting the following features:

1. **High Performance**: Provides fast parameter access, allowing users to freely read and write parameters in the code without worrying about performance issues.
2. **Scope Management**: Manages the definition and use of parameters through scopes, ensuring the isolation and safety of parameter values.
3. **Command Line Integration**: Automatically displays all parameters and their help information in the application's command line.

## Minimal Example

Here is a simple example demonstrating how to use Hyperparameter to build a command-line program:

```rust
use clap::Parser;
use hyperparameter::*;

#[derive(Parser)]
#[command(after_long_help=generate_params_help())]
struct CommandLineArgs {
/// Specifies parameters in the format `-D key=value` on the command line
#[arg(short = 'D', long)]
define: Vec<String>,
}

fn main() {
let args = CommandLineArgs::parse();
with_params! {
params ParamScope::from(&args.define); // Receives all parameters from the command line

// Retrieves the parameter `example.param1`, using a default value of `1` if not specified.
println!("param1={}", get_param!(example.param1, 1));
// Retrieves the parameter `example.param2`, displaying help information when `<app> --help` is executed.
println!("param2={}", get_param!(example.param2, false, "help for example.param2"));
}
}
```
When executing `clap_mini --help`, a section `Hyperparameters` appears at the end of the help information, explaining the names of hyperparameters and their help information:

```
Usage: clap_mini [OPTIONS]

Options:
-D, --define <DEFINE>
Specifies hyperparameters in the format `-D key=value` via the command line

-h, --help
Print help (see a summary with '-h')

Hyperparameters:
example.param2
help for example.param2
```
Following the prompt, you can specify the parameter value using `-D example.param2=<value>`:

```shell
$ clap_mini # Default values
param1=1
param2=false

$ clap_mini -D example.param2=true
param1=1
param2=true
```

## Using Configuration Files

Hyperparameter also supports the use of configuration files. The following example shows how to integrate configuration files, command-line parameters, and user-defined configurations:

```rust
use std::path::Path;

use clap::Parser;
use config::{self, File};
use hyperparameter::*;

#[derive(Parser)]
#[command(after_long_help=generate_params_help())]
struct CommandLineArgs {
/// Specifies parameters in the format `-D key=value` on the command line
#[arg(short = 'D', long)]
define: Vec<String>,

/// Specifies the configuration file path in the format `-C <path>` on the command line
#[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().unwrap();

println!("param1={} // No scope", get_param!(example.param1, "default".to_string()));

with_params! { // Configuration file parameter scope
params config.param_scope();

println!("param1={} // cfg file scope", get_param!(example.param1, "default".to_string()));
with_params! { // Command-line arguments scope
params ParamScope::from(&args.define);

println!("param1={} // cmdline args scope", get_param!(example.param1, "default".to_string(), "Example param1"));
with_params! { // User-defined scope
set example.param1= "scoped".to_string();

println!("param1={} // user-defined scope", get_param!(example.param1, "default".to_string()));
}
}
}
}
```
Directly executing the command `clap_layered` yields the following output:

```
param1=default // No scope # Outside any specific scope
param1=from config // cfg file scope # Entered configuration file scope, parameter value affected by the config file
param1=from config // cmdline args scope # Entered command-line scope, command-line overrides config file
param1=scoped // user-defined scope # Entered user-defined scope, custom value overrides command-line
```
As can be seen:
1. Nested scopes override layer by layer, with parameters in an inner scope overriding those in an outer scope.
2. The command-line scope did not specify the parameter, thus inheriting the value from the outer scope.

If the command line specifies the value of `example.param1`, the following input is obtained:

```shell
$ clap_layered -D example.param1="from cmdline"
param1=default // No scope
param1=from config // cfg file scope
param1=from cmdline // cmdline args scope
param1=scoped // user-defined scope
```
131 changes: 131 additions & 0 deletions examples/rust/README.zh.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
# 高性能Rust配置系统

Hyperparameter 是一个为 Rust 和 Python 设计的高性能配置系统,它支持以下特性:

1. 高性能:提供快速的参数访问,允许用户在代码中自由读写参数,无需担心性能问题。
2. 作用域管理:通过作用域管理参数的定义和使用,确保参数值的隔离和安全。
3. 命令行集成:支持在应用的命令行中自动展示所有参数及其帮助信息。


## 最小示例

以下是一个简单示例,展示如何使用 Hyperparameter 构建一个命令行程序:
```rust
use clap::Parser;
use hyperparameter::*;

#[derive(Parser)]
#[command(after_long_help=generate_params_help())]
struct CommandLineArgs {
/// 在命令行以`-D key=value`格式指定参数
#[arg(short = 'D', long)]
define: Vec<String>,
}

fn main() {
let args = CommandLineArgs::parse();
with_params! {
params ParamScope::from(&args.define); // 从命令行接收全部参数

// 读取参数`example.param1`,若未指定则使用默认值`1`.
println!("param1={}", get_param!(example.param1, 1));
// 读取参数`example.param2`,在执行`<app> --help`时输出帮助信息.
println!("param2={}", get_param!(example.param2, false, "help for example.param2"));
}
}
```
当执行`clap_mini --help`时,在帮助信息结尾出现了`Hyperparameters`一节,说明了超参名称及其帮助信息:
```
Usage: clap_mini [OPTIONS]

Options:
-D, --define <DEFINE>
Specifies hyperparameters in the format `-D key=value` via the command line

-h, --help
Print help (see a summary with '-h')

Hyperparameters:
example.param2
help for example.param2
```
根据提示,可以使用`-D example.param2=<value>`来指定参数取值:
```shell
$ clap_mini # 默认取值
param1=1
param2=false

$ clap_mini -D example.param2=true
param1=1
param2=true
```

## 结合配置文件使用

Hyperparameter 也支持与配置文件结合使用。以下示例展示了如何整合配置文件、命令行参数和用户自定义配置:

```rust
use std::path::Path;

use clap::Parser;
use config::{self, File};
use hyperparameter::*;

#[derive(Parser)]
#[command(after_long_help=generate_params_help())]
struct CommandLineArgs {
/// 在命令行以`-D key=value`格式指定参数
#[arg(short = 'D', long)]
define: Vec<String>,

/// 在命令行以`-C <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().unwrap();

println!("param1={} // No scope", get_param!(example.param1, "default".to_string()));

with_params! { // 配置文件参数作用域
params config.param_scope();

println!("param1={} // cfg file scope", get_param!(example.param1, "default".to_string()));
with_params! { // 命令行参数作用域
params ParamScope::from(&args.define);

println!("param1={} // cmdline args scope", get_param!(example.param1, "default".to_string(), "Example param1"));
with_params! { // 用户自定义作用域
set example.param1= "scoped".to_string();

println!("param1={} // user-defined scope", get_param!(example.param1, "default".to_string()));
}
}
}
}

```
直接执行命令`clap_layered`后得到如下输出:
```
param1=default // No scope # 未进入任何scope
param1=from config // cfg file scope # 进入配置文件scope,参数取值受配置文件影响
param1=from config // cmdline args scope # 进入命令行scope,命令行覆盖配置文件
param1=scoped // user-defined scope # 进入自定义scope,自定义取值覆盖命令行
```
可以看到:
1. 嵌套的scope逐层覆盖,内层scope中参数覆盖外层scope;
2. 命令行scope未指定参数,因此继承了外层scope的取值

若使用命令行指定`example.param1`的取值,则得到如下输入:
```shell
$ clap_layered -D example.param1="from cmdline"
param1=default // No scope
param1=from config // cfg file scope
param1=from cmdline // cmdline args scope
param1=scoped // user-defined scope
```
2 changes: 2 additions & 0 deletions examples/rust/cfg.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[example]
param1 = "from config"
Loading
Loading