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(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}