Skip to main content

zyn_core/extract/
data.rs

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