diff options
Diffstat (limited to 'src/lib.rs')
-rw-r--r-- | src/lib.rs | 88 |
1 files changed, 47 insertions, 41 deletions
@@ -11,9 +11,9 @@ extern crate proc_macro; use proc_macro2::TokenStream; -use proc_macro_error::{abort, abort_call_site, dummy::set_dummy, proc_macro_error, ResultExt}; use quote::{quote, quote_spanned}; -use syn::Token; +use std::convert::TryFrom; +use syn::{Error, Result, Token}; struct Field { name: syn::Member, @@ -33,48 +33,51 @@ enum FieldAttr { } #[proc_macro_derive(Merge, attributes(merge))] -#[proc_macro_error] pub fn merge_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let ast = syn::parse(input).unwrap(); - impl_merge(&ast).into() + impl_merge(&ast) + .unwrap_or_else(Error::into_compile_error) + .into() } -fn impl_merge(ast: &syn::DeriveInput) -> TokenStream { +fn impl_merge(ast: &syn::DeriveInput) -> Result<TokenStream> { let name = &ast.ident; - set_dummy(quote! { - impl ::merge::Merge for #name { - fn merge(&mut self, other: Self) { - unimplemented!() - } - } - }); - if let syn::Data::Struct(syn::DataStruct { ref fields, .. }) = ast.data { impl_merge_for_struct(name, fields) } else { - abort_call_site!("merge::Merge can only be derived for structs") + Err(Error::new_spanned( + ast, + "merge::Merge can only be derived for structs", + )) } } -fn impl_merge_for_struct(name: &syn::Ident, fields: &syn::Fields) -> TokenStream { - let assignments = gen_assignments(fields); +fn impl_merge_for_struct(name: &syn::Ident, fields: &syn::Fields) -> Result<TokenStream> { + let assignments = gen_assignments(fields)?; - quote! { + Ok(quote! { impl ::merge::Merge for #name { fn merge(&mut self, other: Self) { #assignments } } - } + }) } -fn gen_assignments(fields: &syn::Fields) -> TokenStream { - let fields = fields.iter().enumerate().map(Field::from); - let assignments = fields.filter(|f| !f.attrs.skip).map(|f| gen_assignment(&f)); - quote! { +fn gen_assignments(fields: &syn::Fields) -> Result<TokenStream> { + let fields = fields + .iter() + .enumerate() + .map(Field::try_from) + .collect::<Result<Vec<_>>>()?; + let assignments = fields + .iter() + .filter(|f| !f.attrs.skip) + .map(|f| gen_assignment(&f)); + Ok(quote! { #( #assignments )* - } + }) } fn gen_assignment(field: &Field) -> TokenStream { @@ -88,34 +91,27 @@ fn gen_assignment(field: &Field) -> TokenStream { } } -impl From<(usize, &syn::Field)> for Field { - fn from(data: (usize, &syn::Field)) -> Self { +impl TryFrom<(usize, &syn::Field)> for Field { + type Error = syn::Error; + + fn try_from(data: (usize, &syn::Field)) -> std::result::Result<Self, Self::Error> { use syn::spanned::Spanned; let (index, field) = data; - Field { + Ok(Field { name: if let Some(ident) = &field.ident { syn::Member::Named(ident.clone()) } else { syn::Member::Unnamed(index.into()) }, span: field.span(), - attrs: field.attrs.iter().into(), - } + attrs: FieldAttrs::new(field.attrs.iter())?, + }) } } impl FieldAttrs { - fn apply(&mut self, attr: FieldAttr) { - match attr { - FieldAttr::Skip => self.skip = true, - FieldAttr::Strategy(path) => self.strategy = Some(path), - } - } -} - -impl<'a, I: Iterator<Item = &'a syn::Attribute>> From<I> for FieldAttrs { - fn from(iter: I) -> Self { + fn new<'a, I: Iterator<Item = &'a syn::Attribute>>(iter: I) -> Result<Self> { let mut field_attrs = Self::default(); for attr in iter { @@ -124,12 +120,19 @@ impl<'a, I: Iterator<Item = &'a syn::Attribute>> From<I> for FieldAttrs { } let parser = syn::punctuated::Punctuated::<FieldAttr, Token![,]>::parse_terminated; - for attr in attr.parse_args_with(parser).unwrap_or_abort() { + for attr in attr.parse_args_with(parser)? { field_attrs.apply(attr); } } - field_attrs + Ok(field_attrs) + } + + fn apply(&mut self, attr: FieldAttr) { + match attr { + FieldAttr::Skip => self.skip = true, + FieldAttr::Strategy(path) => self.strategy = Some(path), + } } } @@ -144,7 +147,10 @@ impl syn::parse::Parse for FieldAttr { let path: syn::Path = input.parse()?; Ok(FieldAttr::Strategy(path)) } else { - abort!(name, "Unexpected attribute: {}", name) + Err(Error::new_spanned( + &name, + format!("Unexpected attribute: {}", name), + )) } } } |