darling_core/codegen/
from_meta_impl.rs

1use std::borrow::Cow;
2
3use proc_macro2::TokenStream;
4use quote::{quote, quote_spanned, ToTokens};
5use syn::spanned::Spanned;
6
7use crate::ast::{Data, Fields, Style};
8use crate::codegen::{Field, OuterFromImpl, TraitImpl, Variant};
9use crate::util::Callable;
10
11pub struct FromMetaImpl<'a> {
12    pub base: TraitImpl<'a>,
13    pub from_word: Option<Cow<'a, Callable>>,
14    pub from_none: Option<&'a Callable>,
15    pub from_expr: Option<&'a Callable>,
16    pub derive_syn_parse: bool,
17}
18
19impl ToTokens for FromMetaImpl<'_> {
20    fn to_tokens(&self, tokens: &mut TokenStream) {
21        let base = &self.base;
22
23        let from_word = self.from_word.as_ref().map(|body| {
24            quote_spanned! {body.span()=>
25                fn from_word() -> ::darling::Result<Self> {
26                    ::darling::export::identity::<fn() -> ::darling::Result<Self>>(#body)()
27                }
28            }
29        });
30
31        let from_none = self.from_none.map(|body| {
32            quote_spanned! {body.span()=>
33                fn from_none() -> ::darling::export::Option<Self> {
34                    ::darling::export::identity::<fn() -> ::darling::export::Option<Self>>(#body)()
35                }
36            }
37        });
38
39        let from_expr = self.from_expr.map(|body| {
40            quote_spanned! {body.span()=>
41                fn from_expr(expr: &::darling::export::syn::Expr) -> ::darling::Result<Self> {
42                    ::darling::export::identity::<fn(&::darling::export::syn::Expr) -> ::darling::Result<Self>>(#body)(expr)
43                }
44            }
45        });
46
47        let impl_block = match base.data {
48            // Unit structs allow empty bodies only.
49            Data::Struct(ref vd) if vd.style.is_unit() => {
50                let ty_ident = base.ident;
51                quote!(
52                    fn from_word() -> ::darling::Result<Self> {
53                        ::darling::export::Ok(#ty_ident)
54                    }
55                )
56            }
57
58            // Newtype structs proxy to the sole value they contain.
59            Data::Struct(Fields {
60                ref fields,
61                style: Style::Tuple,
62                ..
63            }) if fields.len() == 1 => {
64                let ty_ident = base.ident;
65                quote!(
66                    fn from_meta(__item: &::darling::export::syn::Meta) -> ::darling::Result<Self> {
67                        ::darling::FromMeta::from_meta(__item)
68                            .map_err(|e| e.with_span(&__item))
69                            .map(#ty_ident)
70                    }
71                )
72            }
73            Data::Struct(Fields {
74                style: Style::Tuple,
75                ..
76            }) => {
77                panic!("Multi-field tuples are not supported");
78            }
79            Data::Struct(ref data) => {
80                let inits = data.fields.iter().map(Field::as_initializer);
81                let declare_errors = base.declare_errors();
82                let require_fields = base.require_fields();
83                let check_errors = base.check_errors();
84                let decls = base.local_declarations();
85                let core_loop = base.core_loop();
86                let default = base.fallback_decl();
87                let post_transform = base.post_transform_call();
88
89                quote!(
90                    #from_word
91
92                    #from_none
93
94                    #from_expr
95
96                    fn from_list(__items: &[::darling::export::NestedMeta]) -> ::darling::Result<Self> {
97
98                        #decls
99
100                        #declare_errors
101
102                        #core_loop
103
104                        #require_fields
105
106                        #check_errors
107
108                        #default
109
110                        ::darling::export::Ok(Self {
111                            #(#inits),*
112                        }) #post_transform
113                    }
114                )
115            }
116            Data::Enum(ref variants) => {
117                let unit_arms = variants.iter().map(Variant::as_unit_match_arm);
118
119                let (unknown_variant_err, unknown_unit_variant_err) = if !variants.is_empty() {
120                    let names = variants.iter().map(Variant::as_name);
121                    let names = quote!(&[#(#names),*]);
122                    (
123                        quote! {
124                            unknown_field_with_alts(__other, #names)
125                        },
126                        quote! {
127                            unknown_value_with_alts(__other, #names)
128                        },
129                    )
130                } else {
131                    (
132                        quote! {
133                            unknown_field(__other)
134                        },
135                        quote!(unknown_value(__other)),
136                    )
137                };
138
139                let data_variants = variants.iter().map(Variant::as_data_match_arm);
140
141                quote!(
142                    fn from_list(__outer: &[::darling::export::NestedMeta]) -> ::darling::Result<Self> {
143                        // An enum must have exactly one value inside the parentheses if it's not a unit
144                        // match arm.
145                        match __outer.len() {
146                            0 => ::darling::export::Err(::darling::Error::too_few_items(1)),
147                            1 => {
148                                if let ::darling::export::NestedMeta::Meta(ref __nested) = __outer[0] {
149                                    match ::darling::util::path_to_string(__nested.path()).as_ref() {
150                                        #(#data_variants)*
151                                        __other => ::darling::export::Err(::darling::Error::#unknown_variant_err.with_span(__nested))
152                                    }
153                                } else {
154                                    ::darling::export::Err(::darling::Error::unsupported_format("literal"))
155                                }
156                            }
157                            _ => ::darling::export::Err(::darling::Error::too_many_items(1)),
158                        }
159                    }
160
161                    fn from_string(lit: &str) -> ::darling::Result<Self> {
162                        match lit {
163                            #(#unit_arms)*
164                            __other => ::darling::export::Err(::darling::Error::#unknown_unit_variant_err)
165                        }
166                    }
167
168                    #from_word
169
170                    #from_none
171
172                    #from_expr
173                )
174            }
175        };
176
177        self.wrap(impl_block, tokens);
178        if self.derive_syn_parse {
179            ParseImpl(self).to_tokens(tokens);
180        }
181    }
182}
183
184impl<'a> OuterFromImpl<'a> for FromMetaImpl<'a> {
185    fn trait_path(&self) -> syn::Path {
186        path!(::darling::FromMeta)
187    }
188
189    fn base(&'a self) -> &'a TraitImpl<'a> {
190        &self.base
191    }
192}
193
194struct ParseImpl<'a>(&'a FromMetaImpl<'a>);
195
196impl<'a> OuterFromImpl<'a> for ParseImpl<'a> {
197    fn trait_path(&self) -> syn::Path {
198        path!(::darling::export::syn::parse::Parse)
199    }
200
201    fn base(&'a self) -> &'a TraitImpl<'a> {
202        &self.0.base
203    }
204
205    fn trait_bound(&self) -> syn::Path {
206        // Since the Parse impl delegates to FromMeta, that's the
207        // trait bound we need to apply.
208        self.0.trait_path()
209    }
210}
211
212impl ToTokens for ParseImpl<'_> {
213    fn to_tokens(&self, tokens: &mut TokenStream) {
214        let from_meta = self.0.trait_path();
215        let impl_block = quote! {
216            fn parse(input: ::darling::export::syn::parse::ParseStream<'_>) -> ::darling::export::syn::Result<Self> {
217                use ::darling::export::IntoIterator;
218
219                let items = ::darling::export::syn::punctuated::Punctuated::<::darling::export::NestedMeta, ::darling::export::syn::Token![,]>::parse_terminated(input)?
220                    .into_iter()
221                    .collect::<::darling::export::Vec<_>>();
222                <Self as #from_meta>::from_list(&items).map_err(::darling::export::Into::into)
223            }
224        };
225
226        self.wrap(impl_block, tokens);
227    }
228}