Skip to main content

ryo_source/pure/to_syn/
item.rs

1//! ToSyn implementations for items and use statements.
2
3use syn::token;
4
5use super::helpers::ident;
6use super::{ToSyn, ToSynError};
7use crate::pure::ast::{PureItem, PureUse, PureUseTree};
8
9impl ToSyn for PureItem {
10    type Output = syn::Item;
11
12    fn to_syn(&self) -> Result<syn::Item, ToSynError> {
13        Ok(match self {
14            PureItem::Use(u) => syn::Item::Use(u.to_syn()?),
15            PureItem::Fn(f) => syn::Item::Fn(f.to_syn()?),
16            PureItem::Struct(s) => syn::Item::Struct(s.to_syn()?),
17            PureItem::Enum(e) => syn::Item::Enum(e.to_syn()?),
18            PureItem::Impl(i) => syn::Item::Impl(i.to_syn()?),
19            PureItem::Const(c) => syn::Item::Const(c.to_syn()?),
20            PureItem::Static(s) => syn::Item::Static(s.to_syn()?),
21            PureItem::Type(t) => syn::Item::Type(t.to_syn()?),
22            PureItem::Mod(m) => syn::Item::Mod(m.to_syn()?),
23            PureItem::Trait(t) => syn::Item::Trait(t.to_syn()?),
24            PureItem::Macro(m) => syn::Item::Macro(m.to_syn()?),
25            PureItem::Other(s) => syn::parse_str(s).map_err(|e| ToSynError::Other {
26                message: format!("Failed to parse item '{}': {}", s, e),
27            })?,
28            // PureItem::Verbatim is never emitted by direct to_syn; the
29            // canonical path is `PureFile::to_source`, which substitutes a
30            // sentinel stub fn beforehand and splices the raw bytes back over
31            // the stub after prettyplease formatting. Anyone reaching this
32            // branch is calling to_syn directly with a Verbatim in the tree,
33            // which has no valid syn::Item representation.
34            PureItem::Verbatim(s) => {
35                return Err(ToSynError::Other {
36                    message: format!(
37                        "PureItem::Verbatim cannot be lowered to syn::Item directly; \
38                         use PureFile::to_source which handles verbatim splicing \
39                         (raw chunk: {:?})",
40                        s.chars().take(40).collect::<String>()
41                    ),
42                });
43            }
44        })
45    }
46}
47
48impl ToSyn for PureUse {
49    type Output = syn::ItemUse;
50
51    fn to_syn(&self) -> Result<syn::ItemUse, ToSynError> {
52        Ok(syn::ItemUse {
53            attrs: vec![],
54            vis: self.vis.to_syn()?,
55            use_token: token::Use::default(),
56            leading_colon: None,
57            tree: self.tree.to_syn()?,
58            semi_token: token::Semi::default(),
59        })
60    }
61}
62
63impl ToSyn for PureUseTree {
64    type Output = syn::UseTree;
65
66    fn to_syn(&self) -> Result<syn::UseTree, ToSynError> {
67        Ok(match self {
68            PureUseTree::Path { path, tree } => syn::UseTree::Path(syn::UsePath {
69                ident: ident(path),
70                colon2_token: token::PathSep::default(),
71                tree: Box::new(tree.to_syn()?),
72            }),
73            PureUseTree::Name(name) => syn::UseTree::Name(syn::UseName { ident: ident(name) }),
74            PureUseTree::Rename { name, rename } => syn::UseTree::Rename(syn::UseRename {
75                ident: ident(name),
76                as_token: token::As::default(),
77                rename: ident(rename),
78            }),
79            PureUseTree::Glob => syn::UseTree::Glob(syn::UseGlob {
80                star_token: token::Star::default(),
81            }),
82            PureUseTree::Group(items) => syn::UseTree::Group(syn::UseGroup {
83                brace_token: token::Brace::default(),
84                items: items.iter().map(|t| t.to_syn()).collect::<Result<_, _>>()?,
85            }),
86        })
87    }
88}
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93    use crate::pure::ast::PureVis;
94    use quote::ToTokens;
95
96    #[test]
97    fn test_pure_use_simple() {
98        let use_stmt = PureUse {
99            vis: PureVis::Private,
100            tree: PureUseTree::Path {
101                path: "std".to_string(),
102                tree: Box::new(PureUseTree::Name("io".to_string())),
103            },
104        };
105        let syn_use = use_stmt.to_syn().unwrap();
106        let output = syn_use.to_token_stream().to_string();
107        assert!(output.contains("std"), "Output: {}", output);
108        assert!(output.contains("io"), "Output: {}", output);
109    }
110
111    #[test]
112    fn test_pure_use_glob() {
113        let use_stmt = PureUse {
114            vis: PureVis::Public,
115            tree: PureUseTree::Path {
116                path: "std".to_string(),
117                tree: Box::new(PureUseTree::Glob),
118            },
119        };
120        let syn_use = use_stmt.to_syn().unwrap();
121        let output = syn_use.to_token_stream().to_string();
122        assert!(output.contains("pub"), "Output: {}", output);
123        assert!(output.contains("*"), "Output: {}", output);
124    }
125
126    #[test]
127    fn test_pure_use_group() {
128        let use_stmt = PureUse {
129            vis: PureVis::Private,
130            tree: PureUseTree::Path {
131                path: "std".to_string(),
132                tree: Box::new(PureUseTree::Group(vec![
133                    PureUseTree::Name("io".to_string()),
134                    PureUseTree::Name("fs".to_string()),
135                ])),
136            },
137        };
138        let syn_use = use_stmt.to_syn().unwrap();
139        let output = syn_use.to_token_stream().to_string();
140        assert!(output.contains("io"), "Output: {}", output);
141        assert!(output.contains("fs"), "Output: {}", output);
142    }
143}