Skip to main content

ryo_source/pure/to_syn/
structs.rs

1//! ToSyn implementations for structs and enums.
2
3use syn::token;
4
5use super::helpers::ident;
6use super::{ToSyn, ToSynError};
7use crate::pure::ast::{PureEnum, PureField, PureFields, PureStruct, PureVariant};
8
9impl ToSyn for PureStruct {
10    type Output = syn::ItemStruct;
11
12    fn to_syn(&self) -> Result<syn::ItemStruct, ToSynError> {
13        Ok(syn::ItemStruct {
14            attrs: self
15                .attrs
16                .iter()
17                .map(|a| a.to_syn())
18                .collect::<Result<Vec<_>, _>>()?,
19            vis: self.vis.to_syn()?,
20            struct_token: token::Struct::default(),
21            ident: ident(&self.name),
22            generics: self.generics.to_syn()?,
23            fields: self.fields.to_syn()?,
24            semi_token: matches!(self.fields, PureFields::Unit | PureFields::Tuple(_))
25                .then(token::Semi::default),
26        })
27    }
28}
29
30impl ToSyn for PureFields {
31    type Output = syn::Fields;
32
33    fn to_syn(&self) -> Result<syn::Fields, ToSynError> {
34        match self {
35            PureFields::Named(fields) => Ok(syn::Fields::Named(syn::FieldsNamed {
36                brace_token: token::Brace::default(),
37                named: fields
38                    .iter()
39                    .map(|f| f.to_syn())
40                    .collect::<Result<_, _>>()?,
41            })),
42            PureFields::Tuple(types) => Ok(syn::Fields::Unnamed(syn::FieldsUnnamed {
43                paren_token: token::Paren::default(),
44                unnamed: types
45                    .iter()
46                    .map(|ty| {
47                        Ok(syn::Field {
48                            attrs: vec![],
49                            vis: syn::Visibility::Inherited,
50                            mutability: syn::FieldMutability::None,
51                            ident: None,
52                            colon_token: None,
53                            ty: ty.to_syn()?,
54                        })
55                    })
56                    .collect::<Result<_, ToSynError>>()?,
57            })),
58            PureFields::Unit => Ok(syn::Fields::Unit),
59        }
60    }
61}
62
63impl ToSyn for PureField {
64    type Output = syn::Field;
65
66    fn to_syn(&self) -> Result<syn::Field, ToSynError> {
67        Ok(syn::Field {
68            attrs: self
69                .attrs
70                .iter()
71                .map(|a| a.to_syn())
72                .collect::<Result<Vec<_>, _>>()?,
73            vis: self.vis.to_syn()?,
74            mutability: syn::FieldMutability::None,
75            ident: Some(ident(&self.name)),
76            colon_token: Some(token::Colon::default()),
77            ty: self.ty.to_syn()?,
78        })
79    }
80}
81
82impl ToSyn for PureEnum {
83    type Output = syn::ItemEnum;
84
85    fn to_syn(&self) -> Result<syn::ItemEnum, ToSynError> {
86        Ok(syn::ItemEnum {
87            attrs: self
88                .attrs
89                .iter()
90                .map(|a| a.to_syn())
91                .collect::<Result<Vec<_>, _>>()?,
92            vis: self.vis.to_syn()?,
93            enum_token: token::Enum::default(),
94            ident: ident(&self.name),
95            generics: self.generics.to_syn()?,
96            brace_token: token::Brace::default(),
97            variants: self
98                .variants
99                .iter()
100                .map(|v| v.to_syn())
101                .collect::<Result<_, _>>()?,
102        })
103    }
104}
105
106impl ToSyn for PureVariant {
107    type Output = syn::Variant;
108
109    fn to_syn(&self) -> Result<syn::Variant, ToSynError> {
110        let discriminant = self
111            .discriminant
112            .as_ref()
113            .map(|d| {
114                let expr: syn::Expr = syn::parse_str(d).map_err(|e| ToSynError::ParseExpr {
115                    input: d.to_string(),
116                    message: e.to_string(),
117                })?;
118                Ok((token::Eq::default(), expr))
119            })
120            .transpose()?;
121
122        Ok(syn::Variant {
123            attrs: self
124                .attrs
125                .iter()
126                .map(|a| a.to_syn())
127                .collect::<Result<Vec<_>, _>>()?,
128            ident: ident(&self.name),
129            fields: self.fields.to_syn()?,
130            discriminant,
131        })
132    }
133}
134
135#[cfg(test)]
136mod tests {
137    use super::*;
138    use crate::pure::ast::{PureGenerics, PureType, PureVis};
139    use quote::ToTokens;
140
141    #[test]
142    fn test_pure_struct_simple() {
143        let s = PureStruct {
144            attrs: vec![],
145            vis: PureVis::Public,
146            name: "Foo".to_string(),
147            generics: PureGenerics::default(),
148            fields: PureFields::Named(vec![PureField {
149                attrs: vec![],
150                vis: PureVis::Private,
151                name: "x".to_string(),
152                ty: PureType::Path("i32".to_string()),
153            }]),
154        };
155        let syn_struct = s.to_syn().unwrap();
156        let output = syn_struct.to_token_stream().to_string();
157        assert!(output.contains("pub"), "Output: {}", output);
158        assert!(output.contains("Foo"), "Output: {}", output);
159        assert!(output.contains("x"), "Output: {}", output);
160    }
161
162    #[test]
163    fn test_pure_struct_tuple() {
164        let s = PureStruct {
165            attrs: vec![],
166            vis: PureVis::Private,
167            name: "Point".to_string(),
168            generics: PureGenerics::default(),
169            fields: PureFields::Tuple(vec![
170                PureType::Path("i32".to_string()),
171                PureType::Path("i32".to_string()),
172            ]),
173        };
174        let syn_struct = s.to_syn().unwrap();
175        let output = syn_struct.to_token_stream().to_string();
176        assert!(output.contains("Point"), "Output: {}", output);
177        assert!(output.contains("i32"), "Output: {}", output);
178    }
179
180    #[test]
181    fn test_pure_enum_simple() {
182        let e = PureEnum {
183            attrs: vec![],
184            vis: PureVis::Public,
185            name: "Status".to_string(),
186            generics: PureGenerics::default(),
187            variants: vec![
188                PureVariant {
189                    attrs: vec![],
190                    name: "Active".to_string(),
191                    fields: PureFields::Unit,
192                    discriminant: None,
193                },
194                PureVariant {
195                    attrs: vec![],
196                    name: "Inactive".to_string(),
197                    fields: PureFields::Unit,
198                    discriminant: None,
199                },
200            ],
201        };
202        let syn_enum = e.to_syn().unwrap();
203        let output = syn_enum.to_token_stream().to_string();
204        assert!(output.contains("Status"), "Output: {}", output);
205        assert!(output.contains("Active"), "Output: {}", output);
206        assert!(output.contains("Inactive"), "Output: {}", output);
207    }
208
209    #[test]
210    fn test_pure_enum_with_discriminant() {
211        let e = PureEnum {
212            attrs: vec![],
213            vis: PureVis::Private,
214            name: "Code".to_string(),
215            generics: PureGenerics::default(),
216            variants: vec![PureVariant {
217                attrs: vec![],
218                name: "A".to_string(),
219                fields: PureFields::Unit,
220                discriminant: Some("1".to_string()),
221            }],
222        };
223        let syn_enum = e.to_syn().unwrap();
224        let output = syn_enum.to_token_stream().to_string();
225        assert!(output.contains("= 1"), "Output: {}", output);
226    }
227}