From 83881bd3633ca6cfe6009ebf6359243b0a2e28fe Mon Sep 17 00:00:00 2001 From: Marli Frost Date: Thu, 25 Jun 2020 20:01:10 +0100 Subject: [PATCH 1/2] feat: generate helpful errors from macro --- dotenv_codegen_implementation/src/lib.rs | 51 ++++++++++++++---------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/dotenv_codegen_implementation/src/lib.rs b/dotenv_codegen_implementation/src/lib.rs index 50a6cc2..31b4882 100644 --- a/dotenv_codegen_implementation/src/lib.rs +++ b/dotenv_codegen_implementation/src/lib.rs @@ -7,41 +7,50 @@ 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(), "expected at least 1 argument"))?; + 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(), + "expected a maximum of 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.value()) { + Ok(val) => Ok(quote!(#val).into()), + Err(VarError::NotPresent) | Err(VarError::NotUnicode(_)) => Err(syn::Error::new( + var_name.span(), + if let Some(lit) = err_msg { + lit.value() + } else { + format!("environment variable `{}` not defined", var_name.value()) + }, + )), } } From 31e48e895244a5aa40e5c9a978928b3ca3c0ef1b Mon Sep 17 00:00:00 2001 From: Marli Frost Date: Sat, 27 Jun 2020 12:58:02 +0100 Subject: [PATCH 2/2] feat: more descriptive errors --- dotenv_codegen_implementation/src/lib.rs | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/dotenv_codegen_implementation/src/lib.rs b/dotenv_codegen_implementation/src/lib.rs index 31b4882..7dd3abe 100644 --- a/dotenv_codegen_implementation/src/lib.rs +++ b/dotenv_codegen_implementation/src/lib.rs @@ -33,23 +33,29 @@ fn expand_env(input_raw: TokenStream) -> syn::Result { let var_name = iter .next() - .ok_or_else(|| syn::Error::new(args.span(), "expected at least 1 argument"))?; + .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() { return Err(syn::Error::new( args.span(), - "expected a maximum of 2 arguments", + "dotenv! takes 1 or 2 arguments", )); } - match env::var(var_name.value()) { + match env::var(&var_name) { Ok(val) => Ok(quote!(#val).into()), - Err(VarError::NotPresent) | Err(VarError::NotUnicode(_)) => Err(syn::Error::new( + Err(e) => Err(syn::Error::new( var_name.span(), - if let Some(lit) = err_msg { - lit.value() - } else { - format!("environment variable `{}` not defined", var_name.value()) + match (e, err_msg) { + (_, Some(lit)) => lit.value(), + (VarError::NotPresent, _) => { + format!("environment variable `{}` not defined", var_name) + } + (VarError::NotUnicode(s), _) => format!( + "environment variable `{}` was not valid unicode: {:?}", + var_name, s + ), }, )), }