diff --git a/dotenv_codegen_implementation/src/lib.rs b/dotenv_codegen_implementation/src/lib.rs index 50a6cc2..9e41213 100644 --- a/dotenv_codegen_implementation/src/lib.rs +++ b/dotenv_codegen_implementation/src/lib.rs @@ -7,41 +7,58 @@ use proc_macro_hack::proc_macro_hack; use quote::quote; use syn::parse::Parser; use syn::punctuated::Punctuated; +use syn::spanned::Spanned; 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 msg = format!("Error loading .env file: {}", err); + return quote! { + compile_error!(#msg); + } + .into(); } - // Either everything was fine, or we didn't find an .env file (which we ignore) - expand_env(input) + match expand_env(input) { + Ok(stream) => stream, + Err(e) => e.to_compile_error().into(), + } } -fn expand_env(input_raw: TokenStream) -> TokenStream { - let args = >::parse_terminated - .parse(input_raw) - .expect("expected macro to be called with a comma-separated list of string literals"); +fn expand_env(input_raw: TokenStream) -> syn::Result { + let args = >::parse_terminated.parse(input_raw)?; let mut iter = args.iter(); - let var_name = match iter.next() { - Some(s) => s.value(), - 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 var_name = iter + .next() + .ok_or_else(|| syn::Error::new(args.span(), "dotenv! takes 1 or 2 arguments"))? + .value(); + let err_msg = iter.next(); if iter.next().is_some() { - panic!("expected 1 or 2 arguments, found 3 or more"); + return Err(syn::Error::new( + args.span(), + "dotenv! takes 1 or 2 arguments", + )); } - match env::var(var_name) { - Ok(val) => quote!(#val).into(), - Err(VarError::NotPresent) | Err(VarError::NotUnicode(_)) => panic!("{}", err_msg), + match env::var(&var_name) { + Ok(val) => Ok(quote!(#val).into()), + Err(e) => Err(syn::Error::new( + var_name.span(), + err_msg.map_or_else( + || match e { + VarError::NotPresent => { + format!("environment variable `{}` not defined", var_name) + } + VarError::NotUnicode(s) => format!( + "environment variable `{}` was not valid unicode: {:?}", + var_name, s + ), + }, + |lit| lit.value(), + ), + )), } }