Skip to content

Commit

Permalink
Merge pull request #14 from dariocurr/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
dariocurr authored Sep 16, 2023
2 parents 627f2a8 + a03bda6 commit cb3db6e
Show file tree
Hide file tree
Showing 14 changed files with 542 additions and 333 deletions.
19 changes: 7 additions & 12 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v3.6.0
- uses: actions/checkout@v4.0.0
with:
ref: ${{ github.ref_name }}

- name: Cache Rust
uses: actions/[email protected].1
uses: actions/[email protected].2
with:
path: |
~/.cargo
Expand All @@ -37,11 +37,6 @@ jobs:
- name: Install cargo-release
run: cargo install cargo-release

- name: Setup git
run: |
git config user.name "github-actions[bot]"
git config user.email "[email protected]"
- name: Cargo login
run: cargo login ${{ secrets.CRATES_API_TOKEN }}

Expand All @@ -59,7 +54,7 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v3.6.0
- uses: actions/checkout@v4.0.0

- name: Create Github release
uses: softprops/action-gh-release@v1
Expand All @@ -77,10 +72,10 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v3.6.0
- uses: actions/checkout@v4.0.0

- name: Cache Rust
uses: actions/[email protected].1
uses: actions/[email protected].2
with:
path: |
~/.cargo
Expand Down Expand Up @@ -115,7 +110,7 @@ jobs:
needs: release
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3.6.0
- uses: actions/checkout@v4.0.0

- name: Merge ${{ github.base_ref }} -> develop
uses: devmasx/[email protected]
Expand All @@ -125,7 +120,7 @@ jobs:
github_token: ${{ github.token }}
type: now

- uses: actions/checkout@v3.6.0
- uses: actions/checkout@v4.0.0
with:
ref: develop

Expand Down
14 changes: 7 additions & 7 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v3.6.0
- uses: actions/checkout@v4.0.0

- name: Cache Rust
uses: actions/[email protected].1
uses: actions/[email protected].2
with:
path: |
~/.cargo
Expand All @@ -54,10 +54,10 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v3.6.0
- uses: actions/checkout@v4.0.0

- name: Cache Rust
uses: actions/[email protected].1
uses: actions/[email protected].2
with:
path: |
~/.cargo
Expand Down Expand Up @@ -89,7 +89,7 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v3.6.0
- uses: actions/checkout@v4.0.0

- name: Check formatting
run: cargo fmt --check
Expand All @@ -103,10 +103,10 @@ jobs:
os: [ubuntu-latest]
timeout-minutes: 15
steps:
- uses: actions/checkout@v3.6.0
- uses: actions/checkout@v4.0.0

- name: Cache Rust
uses: actions/[email protected].1
uses: actions/[email protected].2
with:
path: |
~/.cargo
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ members = [
"env-settings-utils",
"test-env-settings",
]
resolver = "2"

[workspace.package]
authors = ["Dario Curreri <[email protected]>"]
Expand Down
14 changes: 11 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,27 +77,34 @@ struct MyStruct {
#[env_settings(variable = "MY_BIRTH_DATE")]
birth_date: String,

birth_place: Option<String>
birth_place: Option<String>,

#[env_settings(skip)]
friends: Vec<String>,
}

fn main() {
let my_struct = MyStruct::from_env().unwrap();
let friends = vec!["luca".to_string()];
let my_struct = MyStruct::from_env(friends.clone()).unwrap();
assert_eq!(my_struct.name, "paolo".to_string());
assert_eq!(my_struct.favourite_number, 42);
assert_eq!(my_struct.birth_date, "01/01/1970");
assert_eq!(my_struct.birth_place, None);
assert_eq!(my_struct.friends, friends);

let name = "luca";
let my_struct = MyStruct::new(
Some(name.to_string()),
None,
None,
Some("london".to_string())
Some("london".to_string()),
friends.clone(),
).unwrap();
assert_eq!(my_struct.name, name);
assert_eq!(my_struct.favourite_number, 42);
assert_eq!(my_struct.birth_date, "01/01/1970");
assert_eq!(my_struct.birth_place, Some("london".to_string()));
assert_eq!(my_struct.friends, friends);
}
```

Expand All @@ -117,6 +124,7 @@ The current supported parameters for the structs are:
The current supported parameters for the fields are:

- `default`: the default value to use if the environment variable is not found. By default, it is not set
- `skip`: whether to skip the parsing of the environment variable
- `variable`: the environment variable to use for the lookup. By default, the name of the field

### Variables resolution hierarchy
Expand Down
183 changes: 96 additions & 87 deletions env-settings-derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ use proc_macro::TokenStream;
use quote::quote;
use std::collections::HashMap;
use syn::parse;
use utils::EnvSettingsField;

mod utils;

Expand All @@ -26,12 +25,13 @@ pub fn env_settings_derive(input: TokenStream) -> TokenStream {
}

/// Implement the logic of the derive macro
fn implement(input: &utils::EnvSettingsInput) -> TokenStream {
fn implement(input: &utils::input::EnvSettingsInput) -> TokenStream {
let struct_name = &input.name;

let mut new_args = Vec::new();
let mut new_impls = Vec::new();
let mut from_env_impls = Vec::new();
let mut from_env_args = Vec::new();

let mut env_variables_impls = quote! {};
let mut file_path_impls = quote! {};
Expand Down Expand Up @@ -59,99 +59,108 @@ fn implement(input: &utils::EnvSettingsInput) -> TokenStream {

let prefix = input.params.prefix.clone().unwrap_or(String::default());

for EnvSettingsField {
name,
type_,
default,
is_optional,
variable,
} in &input.fields
{
let mut env_variable = variable.to_owned().unwrap_or(format!("{}{}", prefix, name));
if case_insensitive {
env_variable = env_variable.to_lowercase();
}
let name_string = name.to_string();
let type_string = type_
.iter()
.map(|segment| segment.ident.to_string())
.collect::<Vec<String>>()
.join("::");

// the variable involded must be named `value`
let optional_value_impl = if *is_optional {
quote! { Some(value) }
} else {
quote! { value }
};
for field in &input.fields {
match field {
utils::field::EnvSettingsField::NonParsable { name, type_ } => {
let argument = quote! { #name: #type_ };
new_args.push(argument.clone());
from_env_args.push(argument);
let value = quote! {#name};
new_impls.push(value.clone());
from_env_impls.push(value);
}
utils::field::EnvSettingsField::Parsable {
name,
name_label,
type_,
type_label,
default,
optional_type,
variable,
} => {
let mut env_variable = variable.to_owned().unwrap_or(format!("{}{}", prefix, name));
if case_insensitive {
env_variable = env_variable.to_lowercase();
}

// the variable involded must be named `value_to_parse`
let convert_err_impl = quote! {
return Err(env_settings_utils::EnvSettingsError::Convert(
#name_string,
value_to_parse.to_owned(),
#type_string,
))
};
// the variable involved must be named `value`
let (optional_value_impl, default_value_impl, new_arg_impl, parse_type) =
match optional_type {
Some(optional_type) => (
quote! { Some(value) },
quote! { None },
quote! { #name: #type_ },
optional_type,
),
None => (
quote! { value },
quote! { return Err(env_settings_utils::EnvSettingsError::NotExists(#env_variable)) },
quote! { #name: Option<#type_> },
type_,
),
};

// the variable involved must be named `value_to_parse`
let convert_err_impl = quote! {
return Err(env_settings_utils::EnvSettingsError::Convert(
#name_label,
value_to_parse.to_owned(),
#type_label,
))
};
let default_impl = match default {
Some(value_to_parse) => {
quote! {
match #value_to_parse.parse::<#parse_type>() {
Ok(value) => #optional_value_impl,
Err(_) => {
let value_to_parse = #value_to_parse.to_owned();
#convert_err_impl
}
}
}
}
None => default_value_impl,
};

let default_impl = match default {
Some(value_to_parse) => {
quote! {
match #value_to_parse.parse::<#type_>() {
// the variable involved must be named `value_to_parse`
let parse_impl = quote! {
match value_to_parse.parse::<#parse_type>() {
Ok(value) => #optional_value_impl,
Err(_) => {
let value_to_parse = #value_to_parse.to_owned();
#convert_err_impl
Err(_) => #convert_err_impl
}
};

// the variable involved must be named `env_variables`
let env_value_impl = if input.params.delay {
quote! {
match env_variables.get(#env_variable) {
Some(value_to_parse) => #parse_impl,
None => #default_impl,
}
}
}
}
None => {
if *is_optional {
quote! { None }
} else {
quote! { return Err(env_settings_utils::EnvSettingsError::NotExists(#env_variable)) }
}
}
};

// the variable involded must be named `value_to_parse`
let parse_impl = quote! {
match value_to_parse.parse::<#type_>() {
Ok(value) => #optional_value_impl,
Err(_) => #convert_err_impl
}
};

// the variable involded must be named `env_variables`
let env_value_impl = if input.params.delay {
quote! {
match env_variables.get(#env_variable) {
Some(value_to_parse) => #parse_impl,
None => #default_impl,
}
}
} else {
match env_variables.get(&env_variable) {
Some(value_to_parse) => quote! {
{
let value_to_parse = #value_to_parse.to_owned();
#parse_impl
match env_variables.get(&env_variable) {
Some(value_to_parse) => quote! {
{
let value_to_parse = #value_to_parse.to_owned();
#parse_impl
}
},
None => default_impl,
}
},

None => quote! { #default_impl },
}
};
};

new_impls.push(quote! {
#name: match #name {
Some(value) => #optional_value_impl,
None => #env_value_impl
new_impls.push(quote! {
#name: match #name {
Some(value) => #optional_value_impl,
None => #env_value_impl
}
});
new_args.push(new_arg_impl);
from_env_impls.push(quote! { #name: #env_value_impl });
}
});
new_args.push(quote! { #name: Option<#type_> });
from_env_impls.push(quote! { #name: #env_value_impl });
}
}

let pre_impls = quote! {
Expand All @@ -171,7 +180,7 @@ fn implement(input: &utils::EnvSettingsInput) -> TokenStream {
Ok(instance)
}

fn from_env() -> env_settings_utils::EnvSettingsResult<Self> {
fn from_env(#(#from_env_args),*) -> env_settings_utils::EnvSettingsResult<Self> {
#pre_impls
let instance = Self {
#(#from_env_impls),*
Expand Down
Loading

0 comments on commit cb3db6e

Please sign in to comment.