Skip to content

Commit

Permalink
Merge pull request #12 from yuk1ty/feature/add-renaming
Browse files Browse the repository at this point in the history
Implement rename helper attribute
  • Loading branch information
YushiOMOTE authored Jul 14, 2022
2 parents 753449a + 63163d5 commit 97215b1
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 5 deletions.
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,5 +150,17 @@ struct A {
}
```
## Renaming fields
Load a field with the given name instead of its Rust's field name. This is helpful if the environment variable name and Rust's field name don't match:
```
#[derive(LoadEnv)]
struct A {
x: bool,
#[econf(rename = "ANOTHER_Y")]
y: u64, // will be loaded from an environment variable `ANOTHER_Y`
}
```
License: MIT
41 changes: 36 additions & 5 deletions econf-derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use proc_macro::TokenStream;

use proc_macro2::{Ident, TokenStream as TokenStream2};
use quote::quote;
use syn::{Data, DeriveInput, Field, Fields, Meta, NestedMeta, parse_macro_input};
use syn::{parse_macro_input, Data, DeriveInput, Field, Fields, Lit, Meta, NestedMeta};

#[proc_macro_derive(LoadEnv, attributes(econf))]
pub fn load_env(input: TokenStream) -> TokenStream {
Expand Down Expand Up @@ -34,6 +34,27 @@ fn is_skip(f: &Field) -> bool {
})
}

fn find_renaming(f: &Field) -> Option<String> {
f.attrs
.iter()
.filter(|attr| attr.path.is_ident("econf"))
.filter_map(|attr| match attr.parse_meta().unwrap() {
Meta::List(meta) => Some(meta.nested),
_ => None,
})
.flat_map(|nested| nested.into_iter())
.filter_map(|nested| match nested {
NestedMeta::Meta(Meta::NameValue(value)) if value.ident.to_string() == "rename" => {
Some(value)
}
_ => None,
})
.find_map(|value| match value.lit {
Lit::Str(token) => Some(token.value()),
_ => None,
})
}

fn content(name: &Ident, data: &Data) -> TokenStream2 {
match data {
Data::Struct(data) => match &data.fields {
Expand All @@ -45,8 +66,13 @@ fn content(name: &Ident, data: &Data) -> TokenStream2 {
#ident: self.#ident,
};
}
quote! {
#ident: self.#ident.load(&(path.to_owned() + "_" + stringify!(#ident)), loader),
match find_renaming(f) {
Some(overwritten_name) => quote! {
#ident: self.#ident.load(&(path.to_owned() + "_" + #overwritten_name), loader),
},
None => quote! {
#ident: self.#ident.load(&(path.to_owned() + "_" + stringify!(#ident)), loader),
}
}
});
quote! {
Expand All @@ -62,8 +88,13 @@ fn content(name: &Ident, data: &Data) -> TokenStream2 {
if is_skip(f) {
return quote! { self.#i, };
}
quote! {
self.#i.load(&(path.to_owned() + "_" + &#i.to_string()), loader),
match find_renaming(f) {
Some(overwritten_name) => quote! {
self.#i.load(&(path.to_owned() + "_" + #overwritten_name), loader),
},
None => quote! {
self.#i.load(&(path.to_owned() + "_" + &#i.to_string()), loader),
},
}
});
quote! {
Expand Down
34 changes: 34 additions & 0 deletions econf/tests/basics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,40 @@ fn skipped() {
assert_eq!(a.v3.s, "initial".to_string());
}

#[derive(LoadEnv)]
struct NestedRenamed {
s: String,
}

#[derive(LoadEnv)]
struct Renamed {
v1: bool,
#[econf(rename = "example_1")]
v2: u32,
#[econf(rename = "example_2")]
v3: NestedRenamed,
}

#[test]
fn renamed() {
std::env::set_var("RENAMED_V1", "true");
std::env::set_var("RENAMED_EXAMPLE_1", "42");
std::env::set_var("RENAMED_EXAMPLE_2_S", "renamed text");

let a = Renamed {
v1: false,
v2: 0,
v3: NestedRenamed {
s: "initial".to_string(),
},
};

let a = econf::load(a, "renamed");
assert_eq!(a.v1, true);
assert_eq!(a.v2, 42);
assert_eq!(a.v3.s, "renamed text".to_string());
}

#[derive(LoadEnv)]
struct Net {
n1: IpAddr,
Expand Down

0 comments on commit 97215b1

Please sign in to comment.