Skip to main content

zyn_core/extract/
fields.rs

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