Skip to main content

ryo_source/pure/to_syn/
other.rs

1//! ToSyn implementations for const, static, type alias, mod, trait, and macro items.
2
3use 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        // Determine if this is a file module (mod foo;) or inline module (mod foo { ... })
95        let has_content = !self.items.is_empty();
96
97        // Build content from items
98        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}