Skip to main content

zyn_core/extract/
fields.rs

1use proc_macro2::Span;
2use syn::spanned::Spanned;
3
4use crate::diagnostic::Diagnostics;
5use crate::types::Input;
6
7use super::FromInput;
8
9/// Converts `syn::Fields` into a specific fields representation.
10///
11/// Implementations exist for `syn::Fields` (any kind), `syn::FieldsNamed`
12/// (named only), and `syn::FieldsUnnamed` (tuple only).
13pub trait FromFields: Sized {
14    fn from_fields(fields: syn::Fields) -> crate::Result<Self>;
15}
16
17impl FromFields for syn::Fields {
18    fn from_fields(fields: syn::Fields) -> crate::Result<Self> {
19        Ok(fields)
20    }
21}
22
23impl FromFields for syn::FieldsNamed {
24    fn from_fields(fields: syn::Fields) -> crate::Result<Self> {
25        match fields {
26            syn::Fields::Named(f) => Ok(f),
27            _ => Err(Diagnostics::error(
28                Span::call_site(),
29                "expected named fields",
30            )),
31        }
32    }
33}
34
35impl FromFields for syn::FieldsUnnamed {
36    fn from_fields(fields: syn::Fields) -> crate::Result<Self> {
37        match fields {
38            syn::Fields::Unnamed(f) => Ok(f),
39            _ => Err(Diagnostics::error(
40                Span::call_site(),
41                "expected unnamed fields",
42            )),
43        }
44    }
45}
46
47/// Element extractor that pulls struct fields from the input.
48///
49/// Defaults to `syn::Fields` (accepts any field kind). Parameterize with
50/// `syn::FieldsNamed` or `syn::FieldsUnnamed` to restrict and validate.
51/// Access the inner value via `Deref` or the `inner()` method.
52///
53/// ```ignore
54/// #[zyn::element]
55/// fn my_element(#[zyn(input)] fields: zyn::Fields<syn::FieldsNamed>) -> proc_macro2::TokenStream {
56///     // fields.named — accessed via Deref to syn::FieldsNamed
57/// }
58/// ```
59pub struct Fields<T: FromFields = syn::Fields>(T);
60
61impl<T: FromFields> Fields<T> {
62    /// Consumes the wrapper and returns the inner value.
63    pub fn inner(self) -> T {
64        self.0
65    }
66}
67
68impl<T: FromFields> std::ops::Deref for Fields<T> {
69    type Target = T;
70
71    fn deref(&self) -> &Self::Target {
72        &self.0
73    }
74}
75
76impl<T: FromFields> std::ops::DerefMut for Fields<T> {
77    fn deref_mut(&mut self) -> &mut Self::Target {
78        &mut self.0
79    }
80}
81
82impl<T: FromFields> FromInput for Fields<T> {
83    fn from_input(input: &Input) -> crate::Result<Self> {
84        let raw = match input {
85            Input::Derive(d) => match &d.data {
86                syn::Data::Struct(s) => s.fields.clone(),
87                _ => {
88                    return Err(Diagnostics::error(
89                        d.ident.span(),
90                        "expected struct input for Fields extractor",
91                    ));
92                }
93            },
94            Input::Item(syn::Item::Struct(s)) => s.fields.clone(),
95            _ => {
96                return Err(Diagnostics::error(
97                    input.span(),
98                    "expected struct input for Fields extractor",
99                ));
100            }
101        };
102
103        T::from_fields(raw).map(Fields)
104    }
105}