Skip to main content

zyn_core/extract/
fields.rs

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