From 019cf487dafe0a0742ed2baccda4de4d7d1f1aa5 Mon Sep 17 00:00:00 2001 From: Joseph Harrison-Lim Date: Sat, 22 Aug 2020 17:41:57 -0400 Subject: [PATCH 1/3] Add default value macro --- README.md | 23 +++++++++++- dotenv_codegen/src/lib.rs | 2 +- dotenv_codegen/tests/basic_dotenv_macro.rs | 6 +++ dotenv_codegen_implementation/src/lib.rs | 43 +++++++++++++++++----- 4 files changed, 63 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 1f91ab3..a3e1b9f 100644 --- a/README.md +++ b/README.md @@ -118,7 +118,7 @@ your crate: extern crate dotenv_codegen; ``` -Then, in your crate: +Then, in your crate you may retrieve a value like so: ```rust fn main() { @@ -126,4 +126,25 @@ fn main() { } ``` +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 diff --git a/dotenv_codegen/src/lib.rs b/dotenv_codegen/src/lib.rs index c7588d4..0538e5f 100644 --- a/dotenv_codegen/src/lib.rs +++ b/dotenv_codegen/src/lib.rs @@ -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}; diff --git a/dotenv_codegen/tests/basic_dotenv_macro.rs b/dotenv_codegen/tests/basic_dotenv_macro.rs index 1b4c1c0..d4e1f90 100644 --- a/dotenv_codegen/tests/basic_dotenv_macro.rs +++ b/dotenv_codegen/tests/basic_dotenv_macro.rs @@ -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!"); +} diff --git a/dotenv_codegen_implementation/src/lib.rs b/dotenv_codegen_implementation/src/lib.rs index 50a6cc2..d0d5bc3 100644 --- a/dotenv_codegen_implementation/src/lib.rs +++ b/dotenv_codegen_implementation/src/lib.rs @@ -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) { let args = >::parse_terminated .parse(input_raw) .expect("expected macro to be called with a comma-separated list of string literals"); @@ -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) } From b6357f9c11195fc7000a4dcdf645d93b6878e82a Mon Sep 17 00:00:00 2001 From: Joseph Harrison-Lim Date: Sat, 22 Aug 2020 19:02:07 -0400 Subject: [PATCH 2/3] Add macro failure tests --- .gitignore | 2 ++ dotenv_codegen/Cargo.toml | 3 +++ dotenv_codegen/tests/basic_dotenv_macro.rs | 6 ++++++ dotenv_codegen/tests/ui/dotenv_no_args.rs | 6 ++++++ dotenv_codegen/tests/ui/dotenv_no_args.stderr | 16 ++++++++++++++++ .../tests/ui/dotenv_or_default_no_args.rs | 6 ++++++ .../tests/ui/dotenv_or_default_no_args.stderr | 16 ++++++++++++++++ .../tests/ui/dotenv_or_default_three_args.rs | 6 ++++++ .../tests/ui/dotenv_or_default_three_args.stderr | 16 ++++++++++++++++ dotenv_codegen/tests/ui/dotenv_three_args.rs | 6 ++++++ dotenv_codegen/tests/ui/dotenv_three_args.stderr | 16 ++++++++++++++++ dotenv_codegen_implementation/src/lib.rs | 1 + 12 files changed, 100 insertions(+) create mode 100644 dotenv_codegen/tests/ui/dotenv_no_args.rs create mode 100644 dotenv_codegen/tests/ui/dotenv_no_args.stderr create mode 100644 dotenv_codegen/tests/ui/dotenv_or_default_no_args.rs create mode 100644 dotenv_codegen/tests/ui/dotenv_or_default_no_args.stderr create mode 100644 dotenv_codegen/tests/ui/dotenv_or_default_three_args.rs create mode 100644 dotenv_codegen/tests/ui/dotenv_or_default_three_args.stderr create mode 100644 dotenv_codegen/tests/ui/dotenv_three_args.rs create mode 100644 dotenv_codegen/tests/ui/dotenv_three_args.stderr diff --git a/.gitignore b/.gitignore index 2c96eb1..615c127 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ target/ Cargo.lock +.idea +wip diff --git a/dotenv_codegen/Cargo.toml b/dotenv_codegen/Cargo.toml index 5227f0d..943a2a2 100644 --- a/dotenv_codegen/Cargo.toml +++ b/dotenv_codegen/Cargo.toml @@ -20,3 +20,6 @@ edition = "2018" [dependencies] dotenv_codegen_implementation = { version = "0.15", path = "../dotenv_codegen_implementation" } proc-macro-hack = "0.5" + +[dev-dependencies] +trybuild = "1.0.32" diff --git a/dotenv_codegen/tests/basic_dotenv_macro.rs b/dotenv_codegen/tests/basic_dotenv_macro.rs index d4e1f90..b9c2a50 100644 --- a/dotenv_codegen/tests/basic_dotenv_macro.rs +++ b/dotenv_codegen/tests/basic_dotenv_macro.rs @@ -23,3 +23,9 @@ fn dotenv_or_default_works() { let default_value: &str = dotenv_or_default!("CODEGEN_TEST_NONEXISTING_VARIABLE", "hello!"); assert_eq!(default_value, "hello!"); } + +#[test] +fn ui() { + let t = trybuild::TestCases::new(); + t.compile_fail("tests/ui/*.rs"); +} diff --git a/dotenv_codegen/tests/ui/dotenv_no_args.rs b/dotenv_codegen/tests/ui/dotenv_no_args.rs new file mode 100644 index 0000000..f5a19c8 --- /dev/null +++ b/dotenv_codegen/tests/ui/dotenv_no_args.rs @@ -0,0 +1,6 @@ +#[macro_use] +extern crate dotenv_codegen; + +pub fn main() { + dotenv!(); +} \ No newline at end of file diff --git a/dotenv_codegen/tests/ui/dotenv_no_args.stderr b/dotenv_codegen/tests/ui/dotenv_no_args.stderr new file mode 100644 index 0000000..56419ca --- /dev/null +++ b/dotenv_codegen/tests/ui/dotenv_no_args.stderr @@ -0,0 +1,16 @@ +error: proc-macro derive panicked + --> $DIR/dotenv_no_args.rs:5:5 + | +5 | dotenv!(); + | ^^^^^^^^^^ + | + = help: message: expected 1 or 2 arguments, found none + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: cannot find macro `proc_macro_call` in this scope + --> $DIR/dotenv_no_args.rs:5:5 + | +5 | dotenv!(); + | ^^^^^^^^^^ + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/dotenv_codegen/tests/ui/dotenv_or_default_no_args.rs b/dotenv_codegen/tests/ui/dotenv_or_default_no_args.rs new file mode 100644 index 0000000..d0bd840 --- /dev/null +++ b/dotenv_codegen/tests/ui/dotenv_or_default_no_args.rs @@ -0,0 +1,6 @@ +#[macro_use] +extern crate dotenv_codegen; + +pub fn main() { + dotenv_or_default!(); +} \ No newline at end of file diff --git a/dotenv_codegen/tests/ui/dotenv_or_default_no_args.stderr b/dotenv_codegen/tests/ui/dotenv_or_default_no_args.stderr new file mode 100644 index 0000000..76ae3da --- /dev/null +++ b/dotenv_codegen/tests/ui/dotenv_or_default_no_args.stderr @@ -0,0 +1,16 @@ +error: proc-macro derive panicked + --> $DIR/dotenv_or_default_no_args.rs:5:5 + | +5 | dotenv_or_default!(); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = help: message: expected 1 or 2 arguments, found none + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: cannot find macro `proc_macro_call` in this scope + --> $DIR/dotenv_or_default_no_args.rs:5:5 + | +5 | dotenv_or_default!(); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/dotenv_codegen/tests/ui/dotenv_or_default_three_args.rs b/dotenv_codegen/tests/ui/dotenv_or_default_three_args.rs new file mode 100644 index 0000000..ee9307e --- /dev/null +++ b/dotenv_codegen/tests/ui/dotenv_or_default_three_args.rs @@ -0,0 +1,6 @@ +#[macro_use] +extern crate dotenv_codegen; + +pub fn main() { + dotenv_or_default!("a", "b", "c"); +} \ No newline at end of file diff --git a/dotenv_codegen/tests/ui/dotenv_or_default_three_args.stderr b/dotenv_codegen/tests/ui/dotenv_or_default_three_args.stderr new file mode 100644 index 0000000..d641680 --- /dev/null +++ b/dotenv_codegen/tests/ui/dotenv_or_default_three_args.stderr @@ -0,0 +1,16 @@ +error: proc-macro derive panicked + --> $DIR/dotenv_or_default_three_args.rs:5:5 + | +5 | dotenv_or_default!("a", "b", "c"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: message: expected 1 or 2 arguments, found 3 or more + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: cannot find macro `proc_macro_call` in this scope + --> $DIR/dotenv_or_default_three_args.rs:5:5 + | +5 | dotenv_or_default!("a", "b", "c"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/dotenv_codegen/tests/ui/dotenv_three_args.rs b/dotenv_codegen/tests/ui/dotenv_three_args.rs new file mode 100644 index 0000000..5af2c97 --- /dev/null +++ b/dotenv_codegen/tests/ui/dotenv_three_args.rs @@ -0,0 +1,6 @@ +#[macro_use] +extern crate dotenv_codegen; + +pub fn main() { + dotenv!("a", "b", "c"); +} \ No newline at end of file diff --git a/dotenv_codegen/tests/ui/dotenv_three_args.stderr b/dotenv_codegen/tests/ui/dotenv_three_args.stderr new file mode 100644 index 0000000..fdcf015 --- /dev/null +++ b/dotenv_codegen/tests/ui/dotenv_three_args.stderr @@ -0,0 +1,16 @@ +error: proc-macro derive panicked + --> $DIR/dotenv_three_args.rs:5:5 + | +5 | dotenv!("a", "b", "c"); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: message: expected 1 or 2 arguments, found 3 or more + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: cannot find macro `proc_macro_call` in this scope + --> $DIR/dotenv_three_args.rs:5:5 + | +5 | dotenv!("a", "b", "c"); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/dotenv_codegen_implementation/src/lib.rs b/dotenv_codegen_implementation/src/lib.rs index d0d5bc3..fc5f876 100644 --- a/dotenv_codegen_implementation/src/lib.rs +++ b/dotenv_codegen_implementation/src/lib.rs @@ -41,6 +41,7 @@ pub fn dotenv_or_default(input: TokenStream) -> TokenStream { 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(), From 2dc92722b243620e6538bc34cba1155ec202f202 Mon Sep 17 00:00:00 2001 From: Joseph Harrison-Lim Date: Sat, 22 Aug 2020 23:22:31 -0400 Subject: [PATCH 3/3] \#61 Address review comments --- .gitignore | 2 -- dotenv_codegen/tests/ui/dotenv_no_args.rs | 2 +- .../tests/ui/dotenv_or_default_no_args.rs | 2 +- .../tests/ui/dotenv_or_default_three_args.rs | 2 +- dotenv_codegen/tests/ui/dotenv_three_args.rs | 2 +- dotenv_codegen_implementation/src/lib.rs | 31 +++++++++++++------ 6 files changed, 26 insertions(+), 15 deletions(-) diff --git a/.gitignore b/.gitignore index 615c127..2c96eb1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,2 @@ target/ Cargo.lock -.idea -wip diff --git a/dotenv_codegen/tests/ui/dotenv_no_args.rs b/dotenv_codegen/tests/ui/dotenv_no_args.rs index f5a19c8..4fac807 100644 --- a/dotenv_codegen/tests/ui/dotenv_no_args.rs +++ b/dotenv_codegen/tests/ui/dotenv_no_args.rs @@ -3,4 +3,4 @@ extern crate dotenv_codegen; pub fn main() { dotenv!(); -} \ No newline at end of file +} diff --git a/dotenv_codegen/tests/ui/dotenv_or_default_no_args.rs b/dotenv_codegen/tests/ui/dotenv_or_default_no_args.rs index d0bd840..287c071 100644 --- a/dotenv_codegen/tests/ui/dotenv_or_default_no_args.rs +++ b/dotenv_codegen/tests/ui/dotenv_or_default_no_args.rs @@ -3,4 +3,4 @@ extern crate dotenv_codegen; pub fn main() { dotenv_or_default!(); -} \ No newline at end of file +} diff --git a/dotenv_codegen/tests/ui/dotenv_or_default_three_args.rs b/dotenv_codegen/tests/ui/dotenv_or_default_three_args.rs index ee9307e..05dee0c 100644 --- a/dotenv_codegen/tests/ui/dotenv_or_default_three_args.rs +++ b/dotenv_codegen/tests/ui/dotenv_or_default_three_args.rs @@ -3,4 +3,4 @@ extern crate dotenv_codegen; pub fn main() { dotenv_or_default!("a", "b", "c"); -} \ No newline at end of file +} diff --git a/dotenv_codegen/tests/ui/dotenv_three_args.rs b/dotenv_codegen/tests/ui/dotenv_three_args.rs index 5af2c97..04d584a 100644 --- a/dotenv_codegen/tests/ui/dotenv_three_args.rs +++ b/dotenv_codegen/tests/ui/dotenv_three_args.rs @@ -3,4 +3,4 @@ extern crate dotenv_codegen; pub fn main() { dotenv!("a", "b", "c"); -} \ No newline at end of file +} diff --git a/dotenv_codegen_implementation/src/lib.rs b/dotenv_codegen_implementation/src/lib.rs index fc5f876..5c17823 100644 --- a/dotenv_codegen_implementation/src/lib.rs +++ b/dotenv_codegen_implementation/src/lib.rs @@ -12,7 +12,11 @@ use syn::Token; #[proc_macro_hack] pub fn dotenv(input: TokenStream) -> TokenStream { if let Err(err) = dotenv::dotenv() { - panic!("Error loading .env file: {}", err); + let err_msg = format!("Error loading .env file: {}", err); + return quote! { + compile_error!(#err_msg); + } + .into(); } // Either everything was fine, or we didn't find an .env file (which we ignore) @@ -32,19 +36,28 @@ pub fn dotenv(input: 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); + let err_msg = format!("Error loading .env file: {}", err); + return quote! { + compile_error!(#err_msg); + } + .into(); } // 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(), + match second_value { + Some(default) => match env::var(var_name) { + Ok(val) => quote!(#val).into(), + Err(VarError::NotPresent) | Err(VarError::NotUnicode(_)) => quote!(#default).into(), + }, + None => { + let err_msg = format!("Missing default value for: {}", var_name); + (quote! { + compile_error!(#err_msg) + }) + .into() + } } }