darling_core/options/
forwarded_field.rs

1use quote::{quote, ToTokens, TokenStreamExt};
2use syn::{Ident, Path};
3
4use crate::{Error, FromField, FromMeta};
5
6use super::ParseAttribute;
7
8/// A forwarded field and attributes that influence its behavior.
9#[derive(Debug, Clone)]
10pub struct ForwardedField {
11    /// The ident of the field that will receive the forwarded value.
12    pub ident: Ident,
13    /// Path of the function that will be called to convert the forwarded value
14    /// into the type expected by the field in `ident`.
15    pub with: Option<Path>,
16}
17
18impl ForwardedField {
19    /// Returns a field initializer for this forwarded field.
20    pub fn as_initializer(&self) -> Initializer<'_> {
21        Initializer(self)
22    }
23}
24
25impl FromField for ForwardedField {
26    fn from_field(field: &syn::Field) -> crate::Result<Self> {
27        let result = Self {
28            ident: field.ident.clone().ok_or_else(|| {
29                Error::custom("forwarded field must be named field").with_span(field)
30            })?,
31            with: None,
32        };
33
34        result.parse_attributes(&field.attrs)
35    }
36}
37
38impl ParseAttribute for ForwardedField {
39    fn parse_nested(&mut self, mi: &syn::Meta) -> crate::Result<()> {
40        if mi.path().is_ident("with") {
41            if self.with.is_some() {
42                return Err(Error::duplicate_field_path(mi.path()).with_span(mi));
43            }
44
45            self.with = FromMeta::from_meta(mi)?;
46            Ok(())
47        } else {
48            Err(Error::unknown_field_path_with_alts(mi.path(), &["with"]).with_span(mi))
49        }
50    }
51}
52
53/// A field initializer that assumes:
54///
55/// 1. There is a local variable with the same ident as `self.ident`
56/// 2. That local variable is an `Option`
57/// 3. That any errors were already checked by an accumulator.
58pub struct Initializer<'a>(&'a ForwardedField);
59
60impl ToTokens for Initializer<'_> {
61    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
62        let ident = &self.0.ident;
63        tokens.append_all(quote!(#ident: #ident.expect("Errors were already checked"),));
64    }
65}