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