aboutsummaryrefslogtreecommitdiff
path: root/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib.rs')
-rw-r--r--src/lib.rs88
1 files changed, 47 insertions, 41 deletions
diff --git a/src/lib.rs b/src/lib.rs
index 3727bf6..11f5b49 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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),
+ ))
}
}
}