1use 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}