diff --git a/impl/src/attr.rs b/impl/src/attr.rs new file mode 100644 index 0000000..79ec195 --- /dev/null +++ b/impl/src/attr.rs @@ -0,0 +1,12 @@ +use syn::parse::Nothing; +use syn::{Field, Result}; + +pub fn is_source(field: &Field) -> Result { + for attr in &field.attrs { + if attr.path.is_ident("source") { + syn::parse2::(attr.tokens.clone())?; + return Ok(true); + } + } + Ok(false) +} diff --git a/impl/src/expand.rs b/impl/src/expand.rs index 764e5fb..ac79304 100644 --- a/impl/src/expand.rs +++ b/impl/src/expand.rs @@ -1,8 +1,9 @@ +use crate::attr; use proc_macro2::TokenStream; use quote::quote; use syn::{ - Data, DataEnum, DataStruct, DeriveInput, Error, Fields, FieldsNamed, FieldsUnnamed, Member, - Result, + Data, DataEnum, DataStruct, DeriveInput, Error, Fields, FieldsNamed, FieldsUnnamed, Index, + Member, Result, }; pub fn derive(input: &DeriveInput) -> Result { @@ -21,8 +22,8 @@ fn struct_error(input: &DeriveInput, data: &DataStruct) -> Result { let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); let source = match &data.fields { - Fields::Named(fields) => braced_struct_source(input, fields)?, - Fields::Unnamed(fields) => tuple_struct_source(input, fields)?, + Fields::Named(fields) => braced_struct_source(fields)?, + Fields::Unnamed(fields) => tuple_struct_source(fields)?, Fields::Unit => None, }; @@ -41,16 +42,22 @@ fn struct_error(input: &DeriveInput, data: &DataStruct) -> Result { }) } -fn braced_struct_source(input: &DeriveInput, fields: &FieldsNamed) -> Result> { - let _ = input; - let _ = fields; - unimplemented!() +fn braced_struct_source(fields: &FieldsNamed) -> Result> { + for field in &fields.named { + if attr::is_source(field)? { + return Ok(Some(Member::Named(field.ident.as_ref().unwrap().clone()))); + } + } + Ok(None) } -fn tuple_struct_source(input: &DeriveInput, fields: &FieldsUnnamed) -> Result> { - let _ = input; - let _ = fields; - unimplemented!() +fn tuple_struct_source(fields: &FieldsUnnamed) -> Result> { + for (i, field) in fields.unnamed.iter().enumerate() { + if attr::is_source(field)? { + return Ok(Some(Member::Unnamed(Index::from(i)))); + } + } + Ok(None) } fn enum_error(input: &DeriveInput, data: &DataEnum) -> Result { diff --git a/impl/src/lib.rs b/impl/src/lib.rs index 456c9e5..d524f34 100644 --- a/impl/src/lib.rs +++ b/impl/src/lib.rs @@ -1,11 +1,12 @@ extern crate proc_macro; +mod attr; mod expand; use proc_macro::TokenStream; use syn::{parse_macro_input, DeriveInput}; -#[proc_macro_derive(Error)] +#[proc_macro_derive(Error, attributes(source))] pub fn derive_error(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); expand::derive(&input)