Skip to content

Commit

Permalink
feat: support simple equal format
Browse files Browse the repository at this point in the history
  • Loading branch information
sasakiyori committed Oct 26, 2023
1 parent cbb10a0 commit f587ebc
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 10 deletions.
9 changes: 9 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,16 @@
name = "merge-cfg"
version = "0.1.0"
edition = "2021"
authors = ["sasakiyori <[email protected]>"]
license = "MIT"
description = "Merge or cover config based on priorities."
keywords = ["config"]

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[lib]
proc-macro = true

[dependencies]
syn = "2.0"
quote = "1.0"
57 changes: 57 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,59 @@
# merge-cfg

Merge or cover config based on priorities.

## Usage

You can get your configuration from config file in any way at first. Then derive `MergeCfg` to merge your configuration from command line arguments.

```rust
use merge_cfg::MergeCfg;

#[derive(Debug, MergeCfg)]
struct Config {
id: u64,
name: String,
}

fn main() {
let mut cfg = Config {
id: 1,
name: "abc".to_string(),
};
cfg.merge_cfg();
println!("{:?}", cfg);
}
```

Command line arguments format: `{field}={value}`, field name should be the same with what you defined in your structure:

```shell
$ cargo run
Config { id: 1, name: "abc" }

$ cargo run id=2
Config { id: 2, name: "abc" }

$ cargo run name=xyz
Config { id: 1, name: "xyz" }

$ cargo run id=2 name=xyz
Config { id: 2, name: "xyz" }
```
## Roadmap
- [ ] Support environment variables
- [ ] Merge priority
- [ ] Merge option (choose which to merge)
- [ ] More command line argument formats
- [x] Simple equal format: `{field}={value}`
- [ ] [getopt](https://en.wikipedia.org/wiki/Getopt) format: `-h --{field} {value}`
- [ ] Various use cases
- [ ] Non-panic function
- [ ] Immutable function
- [ ] Field Alias
- [ ] Support complex field type
- [ ] Document
- [ ] Readme
- [ ] ...
60 changes: 50 additions & 10 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,54 @@
pub fn add(left: usize, right: usize) -> usize {
left + right
}
extern crate proc_macro;

use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, Data, DeriveInput, Fields};

#[cfg(test)]
mod tests {
use super::*;
#[proc_macro_derive(MergeCfg)]
pub fn merge_cfg_derive(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
let struct_ident = &ast.ident;

let mut merge_handle = quote! {};
match ast.data {
Data::Struct(s) => match s.fields {
Fields::Named(fields) => {
for field in &fields.named {
let field_ident = field.ident.as_ref().unwrap();
let field_name = field_ident.to_string();
let field_type = field.ty.clone();
// cast and set value
merge_handle = quote! {
#merge_handle
#field_name => {
self.#field_ident = kv[1].parse::<#field_type>().unwrap()
}
};
}
}
_ => panic!("MergeCfg can only be derived on structs with named fields"),
},
_ => panic!("MergeCfg can only be derived on structs"),
}

#[test]
fn it_works() {
let result = add(2, 2);
assert_eq!(result, 4);
// final expanded code
quote! {
impl #struct_ident {
pub fn merge_cfg(&mut self) {
let args: Vec<String> = ::std::env::args().collect();
if args.len() > 1 {
for arg in args.iter().skip(1) {
let kv: Vec<&str> = arg.split('=').collect();
if kv.len() == 2 {
match kv[0] {
#merge_handle
_ => {}
}
}
}
}
}
}
}
.into()
}

0 comments on commit f587ebc

Please sign in to comment.