1use syn::token;
4
5use super::helpers::{ident, make_abi, try_parse_path};
6use super::{ToSyn, ToSynError};
7use crate::pure::ast::{
8 MacroDelimiter, PureConst, PureMacro, PureMod, PureStatic, PureTrait, PureTraitItem,
9 PureTypeAlias,
10};
11
12impl ToSyn for PureConst {
13 type Output = syn::ItemConst;
14
15 fn to_syn(&self) -> Result<syn::ItemConst, ToSynError> {
16 Ok(syn::ItemConst {
17 attrs: self
18 .attrs
19 .iter()
20 .map(|a| a.to_syn())
21 .collect::<Result<Vec<_>, _>>()?,
22 vis: self.vis.to_syn()?,
23 const_token: token::Const::default(),
24 ident: ident(&self.name),
25 generics: syn::Generics::default(),
26 colon_token: token::Colon::default(),
27 ty: Box::new(self.ty.to_syn()?),
28 eq_token: token::Eq::default(),
29 expr: Box::new(
30 self.value
31 .as_ref()
32 .ok_or_else(|| ToSynError::MissingValue {
33 context: format!("ItemConst '{}' must have a value", self.name),
34 })?
35 .to_syn()?,
36 ),
37 semi_token: token::Semi::default(),
38 })
39 }
40}
41
42impl ToSyn for PureStatic {
43 type Output = syn::ItemStatic;
44
45 fn to_syn(&self) -> Result<syn::ItemStatic, ToSynError> {
46 Ok(syn::ItemStatic {
47 attrs: self
48 .attrs
49 .iter()
50 .map(|a| a.to_syn())
51 .collect::<Result<Vec<_>, _>>()?,
52 vis: self.vis.to_syn()?,
53 static_token: token::Static::default(),
54 mutability: if self.is_mut {
55 syn::StaticMutability::Mut(token::Mut::default())
56 } else {
57 syn::StaticMutability::None
58 },
59 ident: ident(&self.name),
60 colon_token: token::Colon::default(),
61 ty: Box::new(self.ty.to_syn()?),
62 eq_token: token::Eq::default(),
63 expr: Box::new(self.value.to_syn()?),
64 semi_token: token::Semi::default(),
65 })
66 }
67}
68
69impl ToSyn for PureTypeAlias {
70 type Output = syn::ItemType;
71
72 fn to_syn(&self) -> Result<syn::ItemType, ToSynError> {
73 Ok(syn::ItemType {
74 attrs: self
75 .attrs
76 .iter()
77 .map(|a| a.to_syn())
78 .collect::<Result<Vec<_>, _>>()?,
79 vis: self.vis.to_syn()?,
80 type_token: token::Type::default(),
81 ident: ident(&self.name),
82 generics: self.generics.to_syn()?,
83 eq_token: token::Eq::default(),
84 ty: Box::new(self.ty.to_syn()?),
85 semi_token: token::Semi::default(),
86 })
87 }
88}
89
90impl ToSyn for PureMod {
91 type Output = syn::ItemMod;
92
93 fn to_syn(&self) -> Result<syn::ItemMod, ToSynError> {
94 let has_content = !self.items.is_empty();
96
97 let content = if has_content {
99 let all_items: Vec<syn::Item> = self
100 .items
101 .iter()
102 .map(|i| i.to_syn())
103 .collect::<Result<Vec<_>, _>>()?;
104 Some((token::Brace::default(), all_items))
105 } else {
106 None
107 };
108
109 Ok(syn::ItemMod {
110 attrs: self
111 .attrs
112 .iter()
113 .map(|a| a.to_syn())
114 .collect::<Result<Vec<_>, _>>()?,
115 vis: self.vis.to_syn()?,
116 unsafety: None,
117 mod_token: token::Mod::default(),
118 ident: ident(&self.name),
119 content,
120 semi: if has_content {
121 None
122 } else {
123 Some(token::Semi::default())
124 },
125 })
126 }
127}
128
129impl ToSyn for PureTrait {
130 type Output = syn::ItemTrait;
131
132 fn to_syn(&self) -> Result<syn::ItemTrait, ToSynError> {
133 Ok(syn::ItemTrait {
134 attrs: self
135 .attrs
136 .iter()
137 .map(|a| a.to_syn())
138 .collect::<Result<Vec<_>, _>>()?,
139 vis: self.vis.to_syn()?,
140 unsafety: if self.is_unsafe {
141 Some(token::Unsafe::default())
142 } else {
143 None
144 },
145 auto_token: if self.is_auto {
146 Some(token::Auto::default())
147 } else {
148 None
149 },
150 restriction: None,
151 trait_token: token::Trait::default(),
152 ident: ident(&self.name),
153 generics: self.generics.to_syn()?,
154 colon_token: if self.supertraits.is_empty() {
155 None
156 } else {
157 Some(token::Colon::default())
158 },
159 supertraits: self
160 .supertraits
161 .iter()
162 .filter_map(|s| syn::parse_str::<syn::TypeParamBound>(s).ok())
163 .collect(),
164 brace_token: token::Brace::default(),
165 items: self
166 .items
167 .iter()
168 .map(|i| i.to_syn())
169 .collect::<Result<Vec<_>, _>>()?,
170 })
171 }
172}
173
174impl ToSyn for PureTraitItem {
175 type Output = syn::TraitItem;
176
177 fn to_syn(&self) -> Result<syn::TraitItem, ToSynError> {
178 Ok(match self {
179 PureTraitItem::Fn(f) => syn::TraitItem::Fn(syn::TraitItemFn {
180 attrs: f
181 .attrs
182 .iter()
183 .map(|a| a.to_syn())
184 .collect::<Result<Vec<_>, _>>()?,
185 sig: syn::Signature {
186 constness: if f.is_const {
187 Some(token::Const::default())
188 } else {
189 None
190 },
191 asyncness: if f.is_async {
192 Some(token::Async::default())
193 } else {
194 None
195 },
196 unsafety: if f.is_unsafe {
197 Some(token::Unsafe::default())
198 } else {
199 None
200 },
201 abi: f.abi.as_ref().map(|a| make_abi(a)),
202 fn_token: token::Fn::default(),
203 ident: ident(&f.name),
204 generics: f.generics.to_syn()?,
205 paren_token: token::Paren::default(),
206 inputs: f
207 .params
208 .iter()
209 .map(|p| p.to_syn())
210 .collect::<Result<_, _>>()?,
211 variadic: None,
212 output: match &f.ret {
213 Some(ty) => {
214 syn::ReturnType::Type(token::RArrow::default(), Box::new(ty.to_syn()?))
215 }
216 None => syn::ReturnType::Default,
217 },
218 },
219 default: if f.body.stmts.is_empty() {
220 None
221 } else {
222 Some(f.body.to_syn()?)
223 },
224 semi_token: if f.body.stmts.is_empty() {
225 Some(token::Semi::default())
226 } else {
227 None
228 },
229 }),
230 PureTraitItem::Const(c) => syn::TraitItem::Const(syn::TraitItemConst {
231 attrs: c
232 .attrs
233 .iter()
234 .map(|a| a.to_syn())
235 .collect::<Result<Vec<_>, _>>()?,
236 const_token: token::Const::default(),
237 ident: ident(&c.name),
238 generics: syn::Generics::default(),
239 colon_token: token::Colon::default(),
240 ty: c.ty.to_syn()?,
241 default: c
242 .value
243 .as_ref()
244 .map(|v| Ok((token::Eq::default(), v.to_syn()?)))
245 .transpose()?,
246 semi_token: token::Semi::default(),
247 }),
248 PureTraitItem::Type {
249 name,
250 bounds,
251 default,
252 } => syn::TraitItem::Type(syn::TraitItemType {
253 attrs: vec![],
254 type_token: token::Type::default(),
255 ident: ident(name),
256 generics: syn::Generics::default(),
257 colon_token: if bounds.is_empty() {
258 None
259 } else {
260 Some(token::Colon::default())
261 },
262 bounds: bounds
263 .iter()
264 .filter_map(|b| syn::parse_str::<syn::TypeParamBound>(b).ok())
265 .collect(),
266 default: default
267 .as_ref()
268 .map(|ty| Ok((token::Eq::default(), ty.to_syn()?)))
269 .transpose()?,
270 semi_token: token::Semi::default(),
271 }),
272 PureTraitItem::Other(s) => syn::parse_str(s).map_err(|e| ToSynError::Other {
273 message: format!("Failed to parse trait item '{}': {}", s, e),
274 })?,
275 })
276 }
277}
278
279impl ToSyn for PureMacro {
280 type Output = syn::ItemMacro;
281
282 fn to_syn(&self) -> Result<syn::ItemMacro, ToSynError> {
283 let tokens: proc_macro2::TokenStream =
284 self.tokens
285 .parse()
286 .map_err(|e: proc_macro2::LexError| ToSynError::Other {
287 message: format!("Failed to parse macro tokens '{}': {}", self.tokens, e),
288 })?;
289 Ok(syn::ItemMacro {
290 attrs: vec![],
291 ident: None,
292 mac: syn::Macro {
293 path: try_parse_path(&self.path)?,
294 bang_token: token::Not::default(),
295 delimiter: match self.delimiter {
296 MacroDelimiter::Paren => syn::MacroDelimiter::Paren(token::Paren::default()),
297 MacroDelimiter::Brace => syn::MacroDelimiter::Brace(token::Brace::default()),
298 MacroDelimiter::Bracket => {
299 syn::MacroDelimiter::Bracket(token::Bracket::default())
300 }
301 },
302 tokens,
303 },
304 semi_token: Some(token::Semi::default()),
305 })
306 }
307}
308
309#[cfg(test)]
310mod tests {
311 use super::*;
312 use crate::pure::ast::{PureExpr, PureGenerics, PureType, PureVis};
313 use quote::ToTokens;
314
315 #[test]
316 fn test_pure_const() {
317 let c = PureConst {
318 attrs: vec![],
319 vis: PureVis::Public,
320 name: "MAX".to_string(),
321 ty: PureType::Path("i32".to_string()),
322 value: Some(PureExpr::Lit("100".to_string())),
323 };
324 let syn_const = c.to_syn().unwrap();
325 let output = syn_const.to_token_stream().to_string();
326 assert!(output.contains("const"), "Output: {}", output);
327 assert!(output.contains("MAX"), "Output: {}", output);
328 assert!(output.contains("100"), "Output: {}", output);
329 }
330
331 #[test]
332 fn test_pure_static() {
333 let s = PureStatic {
334 attrs: vec![],
335 vis: PureVis::Private,
336 is_mut: true,
337 name: "COUNTER".to_string(),
338 ty: PureType::Path("i32".to_string()),
339 value: PureExpr::Lit("0".to_string()),
340 };
341 let syn_static = s.to_syn().unwrap();
342 let output = syn_static.to_token_stream().to_string();
343 assert!(output.contains("static"), "Output: {}", output);
344 assert!(output.contains("mut"), "Output: {}", output);
345 assert!(output.contains("COUNTER"), "Output: {}", output);
346 }
347
348 #[test]
349 fn test_pure_type_alias() {
350 let t = PureTypeAlias {
351 attrs: vec![],
352 vis: PureVis::Public,
353 name: "Result".to_string(),
354 generics: PureGenerics::default(),
355 ty: PureType::Path("std::result::Result<T, Error>".to_string()),
356 };
357 let syn_type = t.to_syn().unwrap();
358 let output = syn_type.to_token_stream().to_string();
359 assert!(output.contains("type"), "Output: {}", output);
360 assert!(output.contains("Result"), "Output: {}", output);
361 }
362
363 #[test]
364 fn test_pure_mod_declaration() {
365 let m = PureMod {
366 attrs: vec![],
367 vis: PureVis::Public,
368 name: "utils".to_string(),
369 items: vec![],
370 };
371 let syn_mod = m.to_syn().unwrap();
372 let output = syn_mod.to_token_stream().to_string();
373 assert!(output.contains("mod"), "Output: {}", output);
374 assert!(output.contains("utils"), "Output: {}", output);
375 }
376
377 #[test]
378 fn test_pure_trait_simple() {
379 let t = PureTrait {
380 attrs: vec![],
381 vis: PureVis::Public,
382 is_unsafe: false,
383 is_auto: false,
384 name: "Drawable".to_string(),
385 generics: PureGenerics::default(),
386 supertraits: vec![],
387 items: vec![],
388 };
389 let syn_trait = t.to_syn().unwrap();
390 let output = syn_trait.to_token_stream().to_string();
391 assert!(output.contains("trait"), "Output: {}", output);
392 assert!(output.contains("Drawable"), "Output: {}", output);
393 }
394}