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

Default env name #34

Merged
merged 6 commits into from
Oct 14, 2020
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
#### Unreleased
* [breaking] Nested config fields now needs to be marked with #[envconfig(nested = true)]
* Environment variable can be automatically derived from a field name (e.g. `db_host` will be tried to loaded from `DB_HOST` env var)

#### v0.9.1 - 2019-10-09
* Get rid of thiserror dependency

Expand Down
2 changes: 1 addition & 1 deletion envconfig/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@ impl fmt::Display for Error {

impl StdError for Error {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
return None;
None
}
}
18 changes: 8 additions & 10 deletions envconfig/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,16 @@
//! pub http_port: u16,
//! }
//!
//! fn main() {
//! // We assume that those environment variables are set somewhere outside
//! env::set_var("DB_HOST", "localhost");
//! env::set_var("DB_PORT", "5432");
//! // We assume that those environment variables are set somewhere outside
//! env::set_var("DB_HOST", "localhost");
//! env::set_var("DB_PORT", "5432");
//!
//! // Initialize config from environment variables
//! let config = Config::init().unwrap();
//! // Initialize config from environment variables
//! let config = Config::init().unwrap();
//!
//! assert_eq!(config.db_host, "localhost");
//! assert_eq!(config.db_port, Some(5432));
//! assert_eq!(config.http_port, 8080);
//! }
//! assert_eq!(config.db_host, "localhost");
//! assert_eq!(config.db_port, Some(5432));
//! assert_eq!(config.http_port, 8080);
//! ```
//!
//! The library uses `std::str::FromStr` trait to convert environment variables into custom
Expand Down
61 changes: 37 additions & 24 deletions envconfig_derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,19 +60,46 @@ fn gen_field_assign(field: &Field) -> proc_macro2::TokenStream {
let attr = fetch_envconfig_attr_from_field(field);

if let Some(attr) = attr {
// if #[envconfig(...)] is there
let list = fetch_list_from_attr(field, attr);
let from_value = find_item_in_list_or_panic(field, &list, "from");

// If nested attribute is present
let nested_value_opt = find_item_in_list(field, &list, "nested");
if nested_value_opt.is_some() {
return gen_field_assign_for_struct_type(field);
}

let opt_default = find_item_in_list(field, &list, "default");

let field_type = &field.ty;
let from_opt = find_item_in_list(field, &list, "from");
let env_var = match from_opt {
Some(v) => quote! { #v },
None => field_to_env_var(field),
};

if to_s(field_type).starts_with("Option ") {
gen_field_assign_for_optional_type(field, from_value, opt_default)
} else {
gen_field_assign_for_non_optional_type(field, from_value, opt_default)
}
geen(field, env_var, opt_default)
} else {
// if #[envconfig(...)] is not present
let env_var = field_to_env_var(field);
geen(field, env_var, None)
}
}

fn field_to_env_var(field: &Field) -> proc_macro2::TokenStream {
let field_name = field.clone().ident.unwrap().to_string().to_uppercase();
quote! { #field_name }
}

fn geen(
field: &Field,
from: proc_macro2::TokenStream,
opt_default: Option<&Lit>,
) -> proc_macro2::TokenStream {
let field_type = &field.ty;
if to_s(field_type).starts_with("Option ") {
gen_field_assign_for_optional_type(field, from, opt_default)
} else {
gen_field_assign_for_struct_type(field)
gen_field_assign_for_non_optional_type(field, from, opt_default)
}
}

Expand All @@ -90,7 +117,7 @@ fn gen_field_assign_for_struct_type(field: &Field) -> proc_macro2::TokenStream {

fn gen_field_assign_for_optional_type(
field: &Field,
from: &Lit,
from: proc_macro2::TokenStream,
opt_default: Option<&Lit>,
) -> proc_macro2::TokenStream {
let ident = &field.ident;
Expand All @@ -106,7 +133,7 @@ fn gen_field_assign_for_optional_type(

fn gen_field_assign_for_non_optional_type(
field: &Field,
from: &Lit,
from: proc_macro2::TokenStream,
opt_default: Option<&Lit>,
) -> proc_macro2::TokenStream {
let ident = &field.ident;
Expand Down Expand Up @@ -148,20 +175,6 @@ fn fetch_list_from_attr(field: &Field, attr: &Attribute) -> Punctuated<NestedMet
}
}

fn find_item_in_list_or_panic<'l, 'n>(
field: &Field,
list: &'l Punctuated<NestedMeta, Comma>,
item_name: &'n str,
) -> &'l Lit {
find_item_in_list(field, list, item_name).unwrap_or_else(|| {
panic!(
"`envconfig` attribute on field `{}` must contain `{}` item",
field_name(field),
item_name
)
})
}

fn find_item_in_list<'l, 'n>(
field: &Field,
list: &'l Punctuated<NestedMeta, Comma>,
Expand Down
1 change: 0 additions & 1 deletion test_suite/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ use envconfig::Envconfig;

#[derive(Envconfig)]
pub struct Config {
#[envconfig(from = "PORT")]
pub port: u16,

#[envconfig(from = "HOST")]
Expand Down
36 changes: 36 additions & 0 deletions test_suite/tests/default_env_var.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
extern crate envconfig;

use envconfig::{Envconfig, Error};
use std::env;

#[derive(Envconfig)]
pub struct Config {
pub db_host: String,
pub db_port: Option<u16>,
}

fn setup() {
env::remove_var("DB_HOST");
env::remove_var("DB_PORT");
}

#[test]
fn test_derives_env_variable_names_automatically() {
setup();

env::set_var("DB_HOST", "db.example.com");
env::set_var("DB_PORT", "5050");

let config = Config::init_from_env().unwrap();
assert_eq!(config.db_host, "db.example.com");
assert_eq!(config.db_port, Some(5050));
}

#[test]
fn test_var_is_missing() {
setup();

let err = Config::init_from_env().err().unwrap();
let expected_err = Error::EnvVarMissing { name: "DB_HOST" };
assert_eq!(err, expected_err);
}
4 changes: 4 additions & 0 deletions test_suite/tests/nesting.rs → test_suite/tests/nested.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,16 @@ pub struct DBConfig {

#[derive(Envconfig)]
pub struct Config {
#[envconfig(nested = true)]
pub db: DBConfig,
}

#[derive(Envconfig)]
pub struct ConfigDouble {
#[envconfig(nested = true)]
pub db1: DBConfig,

#[envconfig(nested = true)]
pub db2: DBConfig,
}

Expand Down