From 381a230b8115d9d81c988928fdff24bbfb1f2628 Mon Sep 17 00:00:00 2001 From: Dominic Date: Wed, 8 Apr 2020 22:07:33 +0200 Subject: [PATCH] remove panics from expand_method --- gotham_restful_derive/src/method.rs | 70 +++++++++++++++++++---------- 1 file changed, 47 insertions(+), 23 deletions(-) diff --git a/gotham_restful_derive/src/method.rs b/gotham_restful_derive/src/method.rs index 6727171..bc05340 100644 --- a/gotham_restful_derive/src/method.rs +++ b/gotham_restful_derive/src/method.rs @@ -3,8 +3,10 @@ use proc_macro::TokenStream; use proc_macro2::{Ident, TokenStream as TokenStream2}; use quote::{format_ident, quote}; use syn::{ + spanned::Spanned, Attribute, AttributeArgs, + Error, FnArg, ItemFn, Lit, @@ -170,7 +172,7 @@ struct MethodArgument ty : MethodArgumentType } -fn interpret_arg_ty(index : usize, attrs : &[Attribute], name : &str, ty : Type) -> MethodArgumentType +fn interpret_arg_ty(index : usize, attrs : &[Attribute], name : &str, ty : Type) -> Result { let attr = attrs.into_iter() .filter(|arg| arg.path.segments.iter().filter(|path| &path.ident.to_string() == "rest_arg").nth(0).is_some()) @@ -179,39 +181,39 @@ fn interpret_arg_ty(index : usize, attrs : &[Attribute], name : &str, ty : Type) if cfg!(feature = "auth") && (attr.as_deref() == Some("auth") || (attr.is_none() && name == "auth")) { - return match ty { + return Ok(match ty { Type::Reference(ty) => MethodArgumentType::AuthStatusRef(*ty.elem), ty => MethodArgumentType::AuthStatus(ty) - }; + }); } if cfg!(feature = "database") && (attr.as_deref() == Some("connection") || attr.as_deref() == Some("conn") || (attr.is_none() && name == "conn")) { - return MethodArgumentType::DatabaseConnection(match ty { + return Ok(MethodArgumentType::DatabaseConnection(match ty { Type::Reference(ty) => *ty.elem, ty => ty - }); + })); } if index == 0 { return match ty { - Type::Reference(ty) => if ty.mutability.is_none() { MethodArgumentType::StateRef } else { MethodArgumentType::StateMutRef }, - _ => panic!("The first argument, unless some feature is used, has to be a (mutable) reference to gotham::state::State") + Type::Reference(ty) => Ok(if ty.mutability.is_none() { MethodArgumentType::StateRef } else { MethodArgumentType::StateMutRef }), + _ => Err(Error::new(ty.span(), "The first argument, unless some feature is used, has to be a (mutable) reference to gotham::state::State")) }; } - MethodArgumentType::MethodArg(ty) + Ok(MethodArgumentType::MethodArg(ty)) } -fn interpret_arg(index : usize, arg : &PatType) -> MethodArgument +fn interpret_arg(index : usize, arg : &PatType) -> Result { let pat = &arg.pat; let ident = format_ident!("arg{}", index); let orig_name = quote!(#pat); - let ty = interpret_arg_ty(index, &arg.attrs, &orig_name.to_string(), *arg.ty.clone()); + let ty = interpret_arg_ty(index, &arg.attrs, &orig_name.to_string(), *arg.ty.clone())?; - MethodArgument { ident, ty } + Ok(MethodArgument { ident, ty }) } #[cfg(feature = "openapi")] @@ -248,19 +250,20 @@ fn expand_operation_id(_ : &AttributeArgs) -> TokenStream2 quote!() } -pub fn expand_method(method : Method, attrs : TokenStream, item : TokenStream) -> TokenStream +fn expand(method : Method, attrs : TokenStream, item : TokenStream) -> Result { let krate = super::krate(); // parse attributes - let mut method_attrs = parse_macro_input!(attrs as AttributeArgs); + let mut method_attrs = parse_macro_input::parse::(attrs)?; let resource_path = match method_attrs.remove(0) { NestedMeta::Meta(Meta::Path(path)) => path, - _ => panic!("Expected resource name for rest macro") + p => return Err(Error::new(p.span(), "Expected name of the Resource struct this method belongs to")) }; - let resource_name = resource_path.segments.last().expect("Resource name must not be empty").ident.to_string(); + let resource_name = resource_path.segments.last().map(|s| s.ident.to_string()) + .ok_or_else(|| Error::new(resource_path.span(), "Resource name must not be empty"))?; - let fun = parse_macro_input!(item as ItemFn); + let fun = parse_macro_input::parse::(item)?; let fun_ident = &fun.sig.ident; let fun_vis = &fun.vis; @@ -282,10 +285,25 @@ pub fn expand_method(method : Method, attrs : TokenStream, item : TokenStream) - let auth_ident = format_ident!("auth"); // extract arguments into pattern, ident and type - let args : Vec = fun.sig.inputs.iter().enumerate().map(|(i, arg)| match arg { - FnArg::Typed(arg) => interpret_arg(i, arg), - FnArg::Receiver(_) => panic!("didn't expect self parameter") - }).collect(); + let mut args : Vec = Vec::new(); + let mut errors : Vec = Vec::new(); + for (i, arg) in fun.sig.inputs.iter().enumerate() + { + let a = match arg { + FnArg::Typed(arg) => interpret_arg(i, arg), + FnArg::Receiver(_) => Err(Error::new(arg.span(), "Didn't expect self parameter")) + }; + match a { + Ok(a) => args.push(a), + Err(e) => errors.push(e) + } + } + if !errors.is_empty() + { + let mut iter = errors.into_iter(); + let first = iter.nth(0).unwrap(); + return Err(iter.fold(first, |mut e0, e1| { e0.combine(e1); e0 })); + } // extract the generic parameters to use let generics : Vec = args.iter() @@ -355,7 +373,7 @@ pub fn expand_method(method : Method, attrs : TokenStream, item : TokenStream) - let operation_id = expand_operation_id(&method_attrs); // put everything together - let output = quote! { + Ok(quote! { #fun #fun_vis mod #mod_ident @@ -392,6 +410,12 @@ pub fn expand_method(method : Method, attrs : TokenStream, item : TokenStream) - } } - }; - output.into() + }) +} + +pub fn expand_method(method : Method, attrs : TokenStream, item : TokenStream) -> TokenStream +{ + expand(method, attrs, item) + .unwrap_or_else(|err| err.to_compile_error()) + .into() }