ryo_source/pure/to_syn/
attr.rs1use syn::token;
4
5use super::helpers::{ident, try_parse_path};
6use super::{ToSyn, ToSynError};
7use crate::pure::ast::{PureAttrMeta, PureAttribute, PureVis};
8
9impl ToSyn for PureAttribute {
10 type Output = syn::Attribute;
11
12 fn to_syn(&self) -> Result<syn::Attribute, ToSynError> {
13 let meta = match &self.meta {
14 PureAttrMeta::Path => syn::Meta::Path(try_parse_path(&self.path)?),
15 PureAttrMeta::List(args) => {
16 let tokens: proc_macro2::TokenStream =
17 args.parse()
18 .map_err(|e: proc_macro2::LexError| ToSynError::Other {
19 message: format!("Failed to parse attribute args '{}': {}", args, e),
20 })?;
21 syn::Meta::List(syn::MetaList {
22 path: try_parse_path(&self.path)?,
23 delimiter: syn::MacroDelimiter::Paren(token::Paren::default()),
24 tokens,
25 })
26 }
27 PureAttrMeta::NameValue(value) => {
28 let value_expr: syn::Expr =
29 syn::parse_str(value).map_err(|e| ToSynError::Other {
30 message: format!("Failed to parse attribute value '{}': {}", value, e),
31 })?;
32 syn::Meta::NameValue(syn::MetaNameValue {
33 path: try_parse_path(&self.path)?,
34 eq_token: token::Eq::default(),
35 value: value_expr,
36 })
37 }
38 };
39
40 Ok(syn::Attribute {
41 pound_token: token::Pound::default(),
42 style: if self.is_inner {
43 syn::AttrStyle::Inner(token::Not::default())
44 } else {
45 syn::AttrStyle::Outer
46 },
47 bracket_token: token::Bracket::default(),
48 meta,
49 })
50 }
51}
52
53impl ToSyn for PureVis {
54 type Output = syn::Visibility;
55
56 fn to_syn(&self) -> Result<syn::Visibility, ToSynError> {
57 match self {
58 PureVis::Private => Ok(syn::Visibility::Inherited),
59 PureVis::Public => Ok(syn::Visibility::Public(token::Pub::default())),
60 PureVis::Crate => Ok(syn::Visibility::Restricted(syn::VisRestricted {
61 pub_token: token::Pub::default(),
62 paren_token: token::Paren::default(),
63 in_token: None,
64 path: Box::new(syn::Path {
65 leading_colon: None,
66 segments: std::iter::once(syn::PathSegment {
67 ident: ident("crate"),
68 arguments: syn::PathArguments::None,
69 })
70 .collect(),
71 }),
72 })),
73 PureVis::Super => Ok(syn::Visibility::Restricted(syn::VisRestricted {
74 pub_token: token::Pub::default(),
75 paren_token: token::Paren::default(),
76 in_token: None,
77 path: Box::new(syn::Path {
78 leading_colon: None,
79 segments: std::iter::once(syn::PathSegment {
80 ident: ident("super"),
81 arguments: syn::PathArguments::None,
82 })
83 .collect(),
84 }),
85 })),
86 PureVis::In(path) => Ok(syn::Visibility::Restricted(syn::VisRestricted {
87 pub_token: token::Pub::default(),
88 paren_token: token::Paren::default(),
89 in_token: Some(token::In::default()),
90 path: Box::new(try_parse_path(path)?),
91 })),
92 }
93 }
94}
95
96#[cfg(test)]
97mod tests {
98 use super::*;
99 use quote::ToTokens;
100
101 #[test]
102 fn test_pure_attribute_path() {
103 let attr = PureAttribute {
104 path: "test".to_string(),
105 meta: PureAttrMeta::Path,
106 is_inner: false,
107 };
108 let syn_attr = attr.to_syn().unwrap();
109 let output = syn_attr.to_token_stream().to_string();
110 assert!(output.contains("test"), "Output: {}", output);
111 assert!(!output.contains("("), "Should not have parens: {}", output);
112 }
113
114 #[test]
115 fn test_pure_attribute_list() {
116 let attr = PureAttribute {
117 path: "derive".to_string(),
118 meta: PureAttrMeta::List("Debug, Clone".to_string()),
119 is_inner: false,
120 };
121 let syn_attr = attr.to_syn().unwrap();
122 let output = syn_attr.to_token_stream().to_string();
123 assert!(output.contains("derive"), "Output: {}", output);
124 assert!(output.contains("Debug"), "Output: {}", output);
125 assert!(output.contains("Clone"), "Output: {}", output);
126 }
127
128 #[test]
129 fn test_pure_attribute_name_value() {
130 let attr = PureAttribute {
131 path: "doc".to_string(),
132 meta: PureAttrMeta::NameValue("\"This is a comment\"".to_string()),
133 is_inner: false,
134 };
135 let syn_attr = attr.to_syn().unwrap();
136 let output = syn_attr.to_token_stream().to_string();
137 assert!(output.contains("doc"), "Output: {}", output);
138 assert!(output.contains("="), "Output: {}", output);
139 assert!(output.contains("comment"), "Output: {}", output);
140 }
141
142 #[test]
143 fn test_pure_attribute_inner() {
144 let attr = PureAttribute {
145 path: "allow".to_string(),
146 meta: PureAttrMeta::List("unused".to_string()),
147 is_inner: true,
148 };
149 let syn_attr = attr.to_syn().unwrap();
150 let output = syn_attr.to_token_stream().to_string();
151 assert!(
153 output.contains("# !") || output.contains("#!"),
154 "Should be inner: {}",
155 output
156 );
157 }
158
159 #[test]
160 fn test_pure_vis_public() {
161 let vis = PureVis::Public;
162 let syn_vis = vis.to_syn().unwrap();
163 let output = syn_vis.to_token_stream().to_string();
164 assert_eq!(output.trim(), "pub");
165 }
166
167 #[test]
168 fn test_pure_vis_crate() {
169 let vis = PureVis::Crate;
170 let syn_vis = vis.to_syn().unwrap();
171 let output = syn_vis.to_token_stream().to_string();
172 assert!(output.contains("crate"), "Output: {}", output);
173 }
174}