darling_core/options/
outer_from.rs1use quote::ToTokens;
2use syn::spanned::Spanned;
3use syn::{Field, Ident, Meta};
4
5use crate::ast::Data;
6use crate::codegen::ForwardAttrs;
7use crate::options::{
8 Core, DefaultExpression, ForwardAttrsFilter, ForwardedField, ParseAttribute, ParseData,
9};
10use crate::util::PathList;
11use crate::{Error, FromField, FromMeta, Result};
12
13#[derive(Debug, Clone)]
16pub struct OuterFrom {
17 pub ident: Option<Ident>,
19
20 pub attrs: Option<ForwardedField>,
22
23 pub container: Core,
24
25 pub attr_names: PathList,
27
28 pub forward_attrs: Option<ForwardAttrsFilter>,
31
32 pub from_ident: bool,
34}
35
36impl OuterFrom {
37 pub fn start(di: &syn::DeriveInput) -> Result<Self> {
38 Ok(OuterFrom {
39 container: Core::start(di)?,
40 attrs: Default::default(),
41 ident: Default::default(),
42 attr_names: Default::default(),
43 forward_attrs: Default::default(),
44 from_ident: Default::default(),
45 })
46 }
47
48 pub fn as_forward_attrs(&self) -> ForwardAttrs<'_> {
49 ForwardAttrs {
50 field: self.attrs.as_ref(),
51 filter: self.forward_attrs.as_ref(),
52 }
53 }
54}
55
56impl ParseAttribute for OuterFrom {
57 fn parse_nested(&mut self, mi: &Meta) -> Result<()> {
58 let path = mi.path();
59 if path.is_ident("attributes") {
60 self.attr_names = FromMeta::from_meta(mi)?;
61 } else if path.is_ident("forward_attrs") {
62 self.forward_attrs = FromMeta::from_meta(mi)?;
63 } else if path.is_ident("from_ident") {
64 self.container.default = Some(DefaultExpression::Trait {
67 span: path.span(),
70 });
71 self.from_ident = true;
72 } else {
73 return self.container.parse_nested(mi);
74 }
75 Ok(())
76 }
77}
78
79impl ParseData for OuterFrom {
80 fn parse_field(&mut self, field: &Field) -> Result<()> {
81 match field.ident.as_ref().map(|v| v.to_string()).as_deref() {
82 Some("ident") => {
83 self.ident.clone_from(&field.ident);
84 Ok(())
85 }
86 Some("attrs") => {
87 self.attrs = ForwardedField::from_field(field).map(Some)?;
88 Ok(())
89 }
90 _ => self.container.parse_field(field),
91 }
92 }
93
94 fn validate_body(&self, errors: &mut crate::error::Accumulator) {
95 self.container.validate_body(errors);
96 if let Some(attrs) = &self.attrs {
97 if self.forward_attrs.is_none() {
98 let container_name = match &self.container.data {
99 Data::Enum(_) => "enum",
100 Data::Struct(_) => "struct",
101 };
102 errors.push(
103 Error::custom(format!(
104 "field will not be populated because `forward_attrs` is not set on the {}",
105 container_name
106 ))
107 .with_span(&attrs.ident),
108 );
109 }
110 }
111
112 if let Some(ForwardAttrsFilter::Only(fwd)) = &self.forward_attrs {
113 for path in fwd.intersection(&self.attr_names) {
114 errors.push(
115 Error::custom(format!(
116 "attribute path `{}` will not be forwarded because it is also listed in `attributes`",
117 path.to_token_stream()
118 ))
119 .with_span(path),
120 );
121 }
122 };
123 }
124}