Skip to main content

zyn_core/ext/
data.rs

1//! Extension trait for `syn::Data` inspection.
2//!
3//! [`DataExt`] adds variant predicates and conversions to `syn::Data`.
4//!
5//! # Examples
6//!
7//! ```ignore
8//! use zyn::ext::DataExt;
9//!
10//! if data.is_struct() {
11//!     let s = data.as_struct().unwrap();
12//!     // s.fields ...
13//! }
14//! ```
15
16use syn::Data;
17
18/// Extension methods for `syn::Data`.
19///
20/// Provides variant predicates (`is_struct`, `is_enum`, `is_union`)
21/// and conversions (`as_struct`, `as_enum`, `as_union`).
22///
23/// # Examples
24///
25/// ```ignore
26/// use zyn::ext::DataExt;
27///
28/// assert!(data.is_enum());
29/// let e = data.as_enum().unwrap();
30/// // e.variants ...
31/// ```
32pub trait DataExt {
33    /// Returns `true` if this is `Data::Struct`.
34    fn is_struct(&self) -> bool;
35    /// Returns `true` if this is `Data::Enum`.
36    fn is_enum(&self) -> bool;
37    /// Returns `true` if this is `Data::Union`.
38    fn is_union(&self) -> bool;
39    /// Returns the inner `syn::DataStruct` if this is a struct.
40    fn as_struct(&self) -> Option<&syn::DataStruct>;
41    /// Returns the inner `syn::DataEnum` if this is an enum.
42    fn as_enum(&self) -> Option<&syn::DataEnum>;
43    /// Returns the inner `syn::DataUnion` if this is a union.
44    fn as_union(&self) -> Option<&syn::DataUnion>;
45    /// Returns the span of this data item.
46    fn span(&self) -> proc_macro2::Span;
47}
48
49impl DataExt for Data {
50    fn is_struct(&self) -> bool {
51        matches!(self, Self::Struct(_))
52    }
53
54    fn is_enum(&self) -> bool {
55        matches!(self, Self::Enum(_))
56    }
57
58    fn is_union(&self) -> bool {
59        matches!(self, Self::Union(_))
60    }
61
62    fn as_struct(&self) -> Option<&syn::DataStruct> {
63        match self {
64            Self::Struct(s) => Some(s),
65            _ => None,
66        }
67    }
68
69    fn as_enum(&self) -> Option<&syn::DataEnum> {
70        match self {
71            Self::Enum(e) => Some(e),
72            _ => None,
73        }
74    }
75
76    fn as_union(&self) -> Option<&syn::DataUnion> {
77        match self {
78            Self::Union(u) => Some(u),
79            _ => None,
80        }
81    }
82
83    fn span(&self) -> proc_macro2::Span {
84        use syn::spanned::Spanned;
85        match self {
86            Self::Struct(s) => s.struct_token.span,
87            Self::Enum(e) => e.enum_token.span,
88            Self::Union(u) => u.union_token.span(),
89        }
90    }
91}
92
93#[cfg(test)]
94mod tests {
95    use super::*;
96
97    fn data_from(input: &str) -> Data {
98        let di: syn::DeriveInput = syn::parse_str(input).unwrap();
99        di.data
100    }
101
102    mod predicates {
103        use super::*;
104
105        #[test]
106        fn struct_variant() {
107            let data = data_from("struct Foo { x: i32 }");
108            assert!(data.is_struct());
109            assert!(!data.is_enum());
110            assert!(!data.is_union());
111        }
112
113        #[test]
114        fn enum_variant() {
115            let data = data_from("enum Foo { A, B }");
116            assert!(!data.is_struct());
117            assert!(data.is_enum());
118            assert!(!data.is_union());
119        }
120
121        #[test]
122        fn union_variant() {
123            let data = data_from("union Foo { x: i32, y: u32 }");
124            assert!(!data.is_struct());
125            assert!(!data.is_enum());
126            assert!(data.is_union());
127        }
128    }
129
130    mod conversions {
131        use super::*;
132
133        #[test]
134        fn as_struct_some() {
135            let data = data_from("struct Foo { x: i32 }");
136            assert!(data.as_struct().is_some());
137        }
138
139        #[test]
140        fn as_struct_none() {
141            let data = data_from("enum Foo { A }");
142            assert!(data.as_struct().is_none());
143        }
144
145        #[test]
146        fn as_enum_some() {
147            let data = data_from("enum Foo { A, B }");
148            assert!(data.as_enum().is_some());
149        }
150
151        #[test]
152        fn as_enum_none() {
153            let data = data_from("struct Foo;");
154            assert!(data.as_enum().is_none());
155        }
156
157        #[test]
158        fn as_union_some() {
159            let data = data_from("union Foo { x: i32, y: u32 }");
160            assert!(data.as_union().is_some());
161        }
162
163        #[test]
164        fn as_union_none() {
165            let data = data_from("struct Foo;");
166            assert!(data.as_union().is_none());
167        }
168    }
169}