Skip to content

Commit

Permalink
Add default value macro
Browse files Browse the repository at this point in the history
  • Loading branch information
jharrilim committed Aug 22, 2020
1 parent 3c1a77b commit 019cf48
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 11 deletions.
23 changes: 22 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,12 +118,33 @@ your crate:
extern crate dotenv_codegen;
```

Then, in your crate:
Then, in your crate you may retrieve a value like so:

```rust
fn main() {
println!("{}", dotenv!("MEANING_OF_LIFE"));
}
```

You may also panic with a supplied error message if the variable does
not exist:

```rust
fn main() {
dotenv!("A_MISSING_VARIABLE", "This is an error message!");
}
```

Or you can supply a default value if the variable does not exist:

```rust
fn main() {
let meaning_of_life: &str = dotenv_or_default!(
"A_MISSING_VARIABLE",
"42"
);
println!("{}", meaning_of_life);
}
```

[dotenv]: https://github.com/bkeepers/dotenv
2 changes: 1 addition & 1 deletion dotenv_codegen/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use proc_macro_hack::proc_macro_hack;

#[proc_macro_hack]
pub use dotenv_codegen_implementation::dotenv;
pub use dotenv_codegen_implementation::{dotenv, dotenv_or_default};
6 changes: 6 additions & 0 deletions dotenv_codegen/tests/basic_dotenv_macro.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,9 @@ fn two_argument_form_works() {
"'quotes within quotes'"
);
}

#[test]
fn dotenv_or_default_works() {
let default_value: &str = dotenv_or_default!("CODEGEN_TEST_NONEXISTING_VARIABLE", "hello!");
assert_eq!(default_value, "hello!");
}
43 changes: 34 additions & 9 deletions dotenv_codegen_implementation/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,38 @@ pub fn dotenv(input: TokenStream) -> TokenStream {
}

// Either everything was fine, or we didn't find an .env file (which we ignore)
expand_env(input)
let (var_name, second_value) = expand_env(input);

let err_msg = match second_value {
Some(e) => e,
None => format!("environment variable `{}` not defined", var_name),
};

match env::var(var_name) {
Ok(val) => quote!(#val).into(),
Err(VarError::NotPresent) | Err(VarError::NotUnicode(_)) => panic!("{}", err_msg),
}
}

fn expand_env(input_raw: TokenStream) -> TokenStream {
#[proc_macro_hack]
pub fn dotenv_or_default(input: TokenStream) -> TokenStream {
if let Err(err) = dotenv::dotenv() {
panic!("Error loading .env file: {}", err);
}

// Either everything was fine, or we didn't find an .env file (which we ignore)
let (var_name, second_value) = expand_env(input);
let default_val = match second_value {
Some(default) => default,
None => panic!("Missing default value for: {}", var_name),
};
match env::var(var_name) {
Ok(val) => quote!(#val).into(),
Err(VarError::NotPresent) | Err(VarError::NotUnicode(_)) => quote!(#default_val).into(),
}
}

fn expand_env(input_raw: TokenStream) -> (String, Option<String>) {
let args = <Punctuated<syn::LitStr, Token![,]>>::parse_terminated
.parse(input_raw)
.expect("expected macro to be called with a comma-separated list of string literals");
Expand All @@ -31,17 +59,14 @@ fn expand_env(input_raw: TokenStream) -> TokenStream {
None => panic!("expected 1 or 2 arguments, found none"),
};

let err_msg = match iter.next() {
Some(lit) => lit.value(),
None => format!("environment variable `{}` not defined", var_name),
let second_value = match iter.next() {
Some(lit) => Some(lit.value()),
None => None,
};

if iter.next().is_some() {
panic!("expected 1 or 2 arguments, found 3 or more");
}

match env::var(var_name) {
Ok(val) => quote!(#val).into(),
Err(VarError::NotPresent) | Err(VarError::NotUnicode(_)) => panic!("{}", err_msg),
}
(var_name, second_value)
}

0 comments on commit 019cf48

Please sign in to comment.