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