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    pub fn inner(self) -> T {
63        self.0
64    }
65}
66
67impl<T: FromFields> std::ops::Deref for Fields<T> {
68    type Target = T;
69
70    fn deref(&self) -> &Self::Target {
71        &self.0
72    }
73}
74
75impl<T: FromFields> std::ops::DerefMut for Fields<T> {
76    fn deref_mut(&mut self) -> &mut Self::Target {
77        &mut self.0
78    }
79}
80
81impl<T: FromFields> FromInput for Fields<T> {
82    fn from_input(input: &Input) -> crate::Result<Self> {
83        let raw = match input {
84            Input::Derive(d) => match &d.data {
85                syn::Data::Struct(s) => s.fields.clone(),
86                _ => {
87                    return Err(Diagnostics::error(
88                        d.ident.span(),
89                        "expected struct input for Fields extractor",
90                    ));
91                }
92            },
93            Input::Item(syn::Item::Struct(s)) => s.fields.clone(),
94            _ => {
95                return Err(Diagnostics::error(
96                    input.span(),
97                    "expected struct input for Fields extractor",
98                ));
99            }
100        };
101
102        T::from_fields(raw).map(Fields)
103    }
104}