1 use syn::spanned::Spanned; 2 use syn::{Field, Ident, Meta}; 3 4 use crate::codegen::ForwardAttrs; 5 use crate::options::{ 6 AttrsField, Core, DefaultExpression, ForwardAttrsFilter, ParseAttribute, ParseData, 7 }; 8 use crate::util::PathList; 9 use crate::{FromField, FromMeta, Result}; 10 11 /// Reusable base for `FromDeriveInput`, `FromVariant`, `FromField`, and other top-level 12 /// `From*` traits. 13 #[derive(Debug, Clone)] 14 pub struct OuterFrom { 15 /// The field on the target struct which should receive the type identifier, if any. 16 pub ident: Option<Ident>, 17 18 /// The field on the target struct which should receive the type attributes, if any. 19 pub attrs: Option<AttrsField>, 20 21 pub container: Core, 22 23 /// The attribute names that should be searched. 24 pub attr_names: PathList, 25 26 /// The attribute names that should be forwarded. The presence of the word with no additional 27 /// filtering will cause _all_ attributes to be cloned and exposed to the struct after parsing. 28 pub forward_attrs: Option<ForwardAttrsFilter>, 29 30 /// Whether or not the container can be made through conversion from the type `Ident`. 31 pub from_ident: bool, 32 } 33 34 impl OuterFrom { start(di: &syn::DeriveInput) -> Result<Self>35 pub fn start(di: &syn::DeriveInput) -> Result<Self> { 36 Ok(OuterFrom { 37 container: Core::start(di)?, 38 attrs: Default::default(), 39 ident: Default::default(), 40 attr_names: Default::default(), 41 forward_attrs: Default::default(), 42 from_ident: Default::default(), 43 }) 44 } 45 as_forward_attrs(&self) -> ForwardAttrs<'_>46 pub fn as_forward_attrs(&self) -> ForwardAttrs<'_> { 47 ForwardAttrs { 48 field: self.attrs.as_ref(), 49 filter: self.forward_attrs.as_ref(), 50 } 51 } 52 } 53 54 impl ParseAttribute for OuterFrom { parse_nested(&mut self, mi: &Meta) -> Result<()>55 fn parse_nested(&mut self, mi: &Meta) -> Result<()> { 56 let path = mi.path(); 57 if path.is_ident("attributes") { 58 self.attr_names = FromMeta::from_meta(mi)?; 59 } else if path.is_ident("forward_attrs") { 60 self.forward_attrs = FromMeta::from_meta(mi)?; 61 } else if path.is_ident("from_ident") { 62 // HACK: Declaring that a default is present will cause fields to 63 // generate correct code, but control flow isn't that obvious. 64 self.container.default = Some(DefaultExpression::Trait { 65 // Use the span of the `from_ident` keyword so that errors in generated code 66 // caused by this will point back to the correct location. 67 span: path.span(), 68 }); 69 self.from_ident = true; 70 } else { 71 return self.container.parse_nested(mi); 72 } 73 Ok(()) 74 } 75 } 76 77 impl ParseData for OuterFrom { parse_field(&mut self, field: &Field) -> Result<()>78 fn parse_field(&mut self, field: &Field) -> Result<()> { 79 match field.ident.as_ref().map(|v| v.to_string()).as_deref() { 80 Some("ident") => { 81 self.ident.clone_from(&field.ident); 82 Ok(()) 83 } 84 Some("attrs") => { 85 self.attrs = AttrsField::from_field(field).map(Some)?; 86 Ok(()) 87 } 88 _ => self.container.parse_field(field), 89 } 90 } 91 validate_body(&self, errors: &mut crate::error::Accumulator)92 fn validate_body(&self, errors: &mut crate::error::Accumulator) { 93 self.container.validate_body(errors); 94 } 95 } 96