From d610103750a8e55eddbbdad3c3a8fd2bcab44105 Mon Sep 17 00:00:00 2001 From: msrd0 Date: Sun, 12 Apr 2020 19:58:05 +0000 Subject: [PATCH] Idiomatify error handling code in gotham_derive --- gotham_restful_derive/src/lib.rs | 2 ++ gotham_restful_derive/src/method.rs | 23 ++++---------- gotham_restful_derive/src/openapi_type.rs | 38 +++++------------------ gotham_restful_derive/src/request_body.rs | 25 ++++++--------- gotham_restful_derive/src/util.rs | 27 ++++++++++++++++ 5 files changed, 51 insertions(+), 64 deletions(-) create mode 100644 gotham_restful_derive/src/util.rs diff --git a/gotham_restful_derive/src/lib.rs b/gotham_restful_derive/src/lib.rs index 8657978..71017d9 100644 --- a/gotham_restful_derive/src/lib.rs +++ b/gotham_restful_derive/src/lib.rs @@ -4,6 +4,8 @@ use proc_macro::TokenStream; use proc_macro2::TokenStream as TokenStream2; use quote::quote; +mod util; + mod from_body; use from_body::expand_from_body; mod method; diff --git a/gotham_restful_derive/src/method.rs b/gotham_restful_derive/src/method.rs index 110f415..7e50f13 100644 --- a/gotham_restful_derive/src/method.rs +++ b/gotham_restful_derive/src/method.rs @@ -1,3 +1,4 @@ +use crate::util::CollectToResult; use heck::{CamelCase, SnakeCase}; use proc_macro::TokenStream; use proc_macro2::{Ident, Span, TokenStream as TokenStream2}; @@ -320,25 +321,13 @@ fn expand(method : Method, attrs : TokenStream, item : TokenStream) -> Result = Vec::new(); - let mut errors : Vec = Vec::new(); - for (i, arg) in fun.sig.inputs.iter().enumerate() - { - let a = match arg { + let args = fun.sig.inputs.iter() + .enumerate() + .map(|(i, arg)| 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 })); - } + }) + .collect_to_result()?; // extract the generic parameters to use let ty_names = method.type_names(); diff --git a/gotham_restful_derive/src/openapi_type.rs b/gotham_restful_derive/src/openapi_type.rs index 4fe1549..e95ab26 100644 --- a/gotham_restful_derive/src/openapi_type.rs +++ b/gotham_restful_derive/src/openapi_type.rs @@ -1,3 +1,4 @@ +use crate::util::CollectToResult; use proc_macro::TokenStream; use proc_macro2::{ Delimiter, @@ -163,21 +164,9 @@ fn expand_enum(input : ItemEnum) -> Result None => ident.to_string() }; - let mut variants : Vec = Vec::new(); - let mut errors : Vec = Vec::new(); - for variant in input.variants.iter().map(expand_variant) - { - match variant { - Ok(variant) => variants.push(variant), - 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 })); - } + let variants = input.variants.iter() + .map(expand_variant) + .collect_to_result()?; Ok(quote! { impl #generics #krate::OpenapiType for #ident #generics @@ -279,22 +268,9 @@ pub fn expand_struct(input : ItemStruct) -> Result let fields : Vec = match input.fields { Fields::Named(named_fields) => { - let mut fields : Vec = Vec::new(); - let mut errors : Vec = Vec::new(); - for field in named_fields.named.iter().map(expand_field) - { - match field { - Ok(field) => fields.push(field), - 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 })); - } - fields + named_fields.named.iter() + .map(expand_field) + .collect_to_result()? }, Fields::Unnamed(fields) => return Err(Error::new(fields.span(), "Unnamed fields are not supported")), Fields::Unit => Vec::new() diff --git a/gotham_restful_derive/src/request_body.rs b/gotham_restful_derive/src/request_body.rs index 930efa5..6c6f473 100644 --- a/gotham_restful_derive/src/request_body.rs +++ b/gotham_restful_derive/src/request_body.rs @@ -1,6 +1,8 @@ +use crate::util::CollectToResult; use proc_macro::TokenStream; use proc_macro2::TokenStream as TokenStream2; use quote::quote; +use std::iter; use syn::{ parse::{Parse, ParseStream, Result as SynResult}, punctuated::Punctuated, @@ -61,22 +63,13 @@ fn expand(tokens : TokenStream) -> Result let ident = input.ident; let generics = input.generics; - let mut types : Vec = Vec::new(); - let mut errors : Vec = Vec::new(); - for attr in input.attrs.into_iter().filter(|attr| - attr.path.segments.iter().last().map(|segment| segment.ident.to_string()) == Some("supported_types".to_string()) // TODO wtf - ) { - match syn::parse2::(attr.tokens) { - Ok(m) => types.extend(m.0), - 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 })); - } + let types = input.attrs.into_iter() + .filter(|attr| attr.path.segments.iter().last().map(|segment| segment.ident.to_string()) == Some("supported_types".to_string())) + .flat_map(|attr| + syn::parse2::(attr.tokens) + .map(|list| Box::new(list.0.into_iter().map(|mime| Ok(mime))) as Box>>) + .unwrap_or_else(|err| Box::new(iter::once(Err(err))))) + .collect_to_result()?; let types = match types { ref types if types.is_empty() => quote!(None), diff --git a/gotham_restful_derive/src/util.rs b/gotham_restful_derive/src/util.rs new file mode 100644 index 0000000..319830a --- /dev/null +++ b/gotham_restful_derive/src/util.rs @@ -0,0 +1,27 @@ +use syn::Error; + +pub trait CollectToResult +{ + type Item; + + fn collect_to_result(self) -> Result, Error>; +} + +impl CollectToResult for I +where + I : Iterator> +{ + type Item = Item; + + fn collect_to_result(self) -> Result, Error> + { + self.fold(, Error>>::Ok(Vec::new()), |res, code| { + match (code, res) { + (Ok(code), Ok(mut codes)) => { codes.push(code); Ok(codes) }, + (Ok(_), Err(errors)) => Err(errors), + (Err(err), Ok(_)) => Err(err), + (Err(err), Err(mut errors)) => { errors.combine(err); Err(errors) } + } + }) + } +}