1use proc_macro2::Span;
4use syn::punctuated::Punctuated;
5use syn::token;
6
7use super::helpers::{ident, make_abi};
8use super::{ToSyn, ToSynError};
9use crate::pure::ast::{PureBlock, PureFn, PureGenericParam, PureGenerics, PureParam, PureStmt};
10
11impl ToSyn for PureFn {
12 type Output = syn::ItemFn;
13
14 fn to_syn(&self) -> Result<syn::ItemFn, ToSynError> {
15 Ok(syn::ItemFn {
16 attrs: self
17 .attrs
18 .iter()
19 .map(|a| a.to_syn())
20 .collect::<Result<Vec<_>, _>>()?,
21 vis: self.vis.to_syn()?,
22 sig: syn::Signature {
23 constness: if self.is_const {
24 Some(token::Const::default())
25 } else {
26 None
27 },
28 asyncness: if self.is_async {
29 Some(token::Async::default())
30 } else {
31 None
32 },
33 unsafety: if self.is_unsafe {
34 Some(token::Unsafe::default())
35 } else {
36 None
37 },
38 abi: self.abi.as_ref().map(|a| make_abi(a)),
39 fn_token: token::Fn::default(),
40 ident: ident(&self.name),
41 generics: self.generics.to_syn()?,
42 paren_token: token::Paren::default(),
43 inputs: self
44 .params
45 .iter()
46 .map(|p| p.to_syn())
47 .collect::<Result<_, _>>()?,
48 variadic: None,
49 output: match &self.ret {
50 Some(ty) => {
51 syn::ReturnType::Type(token::RArrow::default(), Box::new(ty.to_syn()?))
52 }
53 None => syn::ReturnType::Default,
54 },
55 },
56 block: Box::new(self.body.to_syn()?),
57 })
58 }
59}
60
61impl ToSyn for PureParam {
62 type Output = syn::FnArg;
63
64 fn to_syn(&self) -> Result<syn::FnArg, ToSynError> {
65 Ok(match self {
66 PureParam::SelfValue { is_ref, is_mut } => {
67 let self_ty = if *is_ref {
70 if *is_mut {
71 syn::Type::Reference(syn::TypeReference {
73 and_token: token::And::default(),
74 lifetime: None,
75 mutability: Some(token::Mut::default()),
76 elem: Box::new(syn::Type::Path(syn::TypePath {
77 qself: None,
78 path: syn::Path::from(ident("Self")),
79 })),
80 })
81 } else {
82 syn::Type::Reference(syn::TypeReference {
84 and_token: token::And::default(),
85 lifetime: None,
86 mutability: None,
87 elem: Box::new(syn::Type::Path(syn::TypePath {
88 qself: None,
89 path: syn::Path::from(ident("Self")),
90 })),
91 })
92 }
93 } else {
94 syn::Type::Path(syn::TypePath {
96 qself: None,
97 path: syn::Path::from(ident("Self")),
98 })
99 };
100
101 syn::FnArg::Receiver(syn::Receiver {
102 attrs: vec![],
103 reference: if *is_ref {
104 Some((token::And::default(), None))
105 } else {
106 None
107 },
108 mutability: if *is_mut {
109 Some(token::Mut::default())
110 } else {
111 None
112 },
113 self_token: token::SelfValue::default(),
114 colon_token: None,
115 ty: Box::new(self_ty),
116 })
117 }
118 PureParam::Typed { name, ty } => syn::FnArg::Typed(syn::PatType {
119 attrs: vec![],
120 pat: Box::new(syn::Pat::Ident(syn::PatIdent {
121 attrs: vec![],
122 by_ref: None,
123 mutability: None,
124 ident: ident(name),
125 subpat: None,
126 })),
127 colon_token: token::Colon::default(),
128 ty: Box::new(ty.to_syn()?),
129 }),
130 })
131 }
132}
133
134impl ToSyn for PureGenerics {
135 type Output = syn::Generics;
136
137 fn to_syn(&self) -> Result<syn::Generics, ToSynError> {
138 let params: Punctuated<syn::GenericParam, token::Comma> = self
139 .params
140 .iter()
141 .map(|p| p.to_syn())
142 .collect::<Result<_, _>>()?;
143
144 let where_clause = if self.where_clause.is_empty() {
145 None
146 } else {
147 Some(syn::WhereClause {
148 where_token: token::Where::default(),
149 predicates: self
150 .where_clause
151 .iter()
152 .filter_map(|s| syn::parse_str::<syn::WherePredicate>(s).ok())
153 .collect(),
154 })
155 };
156
157 Ok(syn::Generics {
158 lt_token: if params.is_empty() {
159 None
160 } else {
161 Some(token::Lt::default())
162 },
163 params,
164 gt_token: if self.params.is_empty() {
165 None
166 } else {
167 Some(token::Gt::default())
168 },
169 where_clause,
170 })
171 }
172}
173
174impl ToSyn for PureGenericParam {
175 type Output = syn::GenericParam;
176
177 fn to_syn(&self) -> Result<syn::GenericParam, ToSynError> {
178 Ok(match self {
179 PureGenericParam::Type { name, bounds } => syn::GenericParam::Type(syn::TypeParam {
180 attrs: vec![],
181 ident: ident(name),
182 colon_token: if bounds.is_empty() {
183 None
184 } else {
185 Some(token::Colon::default())
186 },
187 bounds: bounds
188 .iter()
189 .filter_map(|b| syn::parse_str::<syn::TypeParamBound>(b).ok())
190 .collect(),
191 eq_token: None,
192 default: None,
193 }),
194 PureGenericParam::Lifetime { name, bounds } => {
195 syn::GenericParam::Lifetime(syn::LifetimeParam {
196 attrs: vec![],
197 lifetime: syn::Lifetime::new(name, Span::call_site()),
198 colon_token: if bounds.is_empty() {
199 None
200 } else {
201 Some(token::Colon::default())
202 },
203 bounds: bounds
204 .iter()
205 .map(|b| syn::Lifetime::new(b, Span::call_site()))
206 .collect(),
207 })
208 }
209 PureGenericParam::Const { name, ty } => syn::GenericParam::Const(syn::ConstParam {
210 attrs: vec![],
211 const_token: token::Const::default(),
212 ident: ident(name),
213 colon_token: token::Colon::default(),
214 ty: syn::parse_str(ty).map_err(|e| ToSynError::ParseType {
215 input: ty.clone(),
216 message: e.to_string(),
217 })?,
218 eq_token: None,
219 default: None,
220 }),
221 })
222 }
223}
224
225impl ToSyn for PureBlock {
226 type Output = syn::Block;
227
228 fn to_syn(&self) -> Result<syn::Block, ToSynError> {
229 Ok(syn::Block {
230 brace_token: token::Brace::default(),
231 stmts: self
232 .stmts
233 .iter()
234 .map(|s| s.to_syn())
235 .collect::<Result<Vec<_>, _>>()?,
236 })
237 }
238}
239
240impl ToSyn for PureStmt {
241 type Output = syn::Stmt;
242
243 fn to_syn(&self) -> Result<syn::Stmt, ToSynError> {
244 match self {
245 PureStmt::Local { pattern, ty, init } => {
246 let pat = if let Some(ty) = ty {
247 syn::Pat::Type(syn::PatType {
248 attrs: vec![],
249 pat: Box::new(pattern.to_syn()?),
250 colon_token: token::Colon::default(),
251 ty: Box::new(ty.to_syn()?),
252 })
253 } else {
254 pattern.to_syn()?
255 };
256 let local_init = init
257 .as_ref()
258 .map(|e| {
259 Ok(syn::LocalInit {
260 eq_token: token::Eq::default(),
261 expr: Box::new(e.to_syn()?),
262 diverge: None,
263 })
264 })
265 .transpose()?;
266 Ok(syn::Stmt::Local(syn::Local {
267 attrs: vec![],
268 let_token: token::Let::default(),
269 pat,
270 init: local_init,
271 semi_token: token::Semi::default(),
272 }))
273 }
274 PureStmt::Semi(expr) => Ok(syn::Stmt::Expr(
275 expr.to_syn()?,
276 Some(token::Semi::default()),
277 )),
278 PureStmt::Expr(expr) => Ok(syn::Stmt::Expr(expr.to_syn()?, None)),
279 PureStmt::Item(item) => Ok(syn::Stmt::Item(item.to_syn()?)),
280 }
281 }
282}
283
284#[cfg(test)]
285mod tests {
286 use super::*;
287 use crate::pure::ast::{PureExpr, PureType, PureVis};
288 use quote::ToTokens;
289
290 #[test]
291 fn test_pure_fn_simple() {
292 let f = PureFn {
293 attrs: vec![],
294 vis: PureVis::Public,
295 is_async: false,
296 is_async_inferred: false,
297 is_const: false,
298 is_unsafe: false,
299 abi: None,
300 name: "foo".to_string(),
301 generics: PureGenerics::default(),
302 params: vec![],
303 ret: None,
304 body: PureBlock::default(),
305 };
306 let syn_fn = f.to_syn().unwrap();
307 let output = syn_fn.to_token_stream().to_string();
308 assert!(output.contains("pub"), "Output: {}", output);
309 assert!(output.contains("foo"), "Output: {}", output);
310 }
311
312 #[test]
313 fn test_pure_fn_with_params() {
314 let f = PureFn {
315 attrs: vec![],
316 vis: PureVis::Private,
317 is_async: true,
318 is_async_inferred: false,
319 is_const: false,
320 is_unsafe: false,
321 abi: None,
322 name: "bar".to_string(),
323 generics: PureGenerics::default(),
324 params: vec![PureParam::Typed {
325 name: "x".to_string(),
326 ty: PureType::Path("i32".to_string()),
327 }],
328 ret: Some(PureType::Path("i32".to_string())),
329 body: PureBlock::default(),
330 };
331 let syn_fn = f.to_syn().unwrap();
332 let output = syn_fn.to_token_stream().to_string();
333 assert!(output.contains("async"), "Output: {}", output);
334 assert!(output.contains("i32"), "Output: {}", output);
335 }
336
337 #[test]
338 fn test_pure_block_with_stmts() {
339 let block = PureBlock {
340 stmts: vec![PureStmt::Local {
341 pattern: crate::pure::ast::PurePattern::Ident {
342 name: "x".to_string(),
343 is_mut: false,
344 },
345 ty: None,
346 init: Some(PureExpr::Lit("42".to_string())),
347 }],
348 };
349 let syn_block = block.to_syn().unwrap();
350 let output = syn_block.to_token_stream().to_string();
351 assert!(output.contains("let"), "Output: {}", output);
352 assert!(output.contains("42"), "Output: {}", output);
353 }
354
355 #[test]
356 fn test_pure_fn_with_extern_c_abi() {
357 let f = PureFn {
358 attrs: vec![],
359 vis: PureVis::Public,
360 is_async: false,
361 is_async_inferred: false,
362 is_const: false,
363 is_unsafe: false,
364 abi: Some("C".to_string()),
365 name: "c_func".to_string(),
366 generics: PureGenerics::default(),
367 params: vec![PureParam::Typed {
368 name: "x".to_string(),
369 ty: PureType::Path("i32".to_string()),
370 }],
371 ret: Some(PureType::Path("i32".to_string())),
372 body: PureBlock::default(),
373 };
374 let syn_fn = f.to_syn().unwrap();
375 let output = syn_fn.to_token_stream().to_string();
376 assert!(
377 output.contains(r#"extern "C""#),
378 r#"Output should contain 'extern "C"': {}"#,
379 output
380 );
381 assert!(output.contains("c_func"), "Output: {}", output);
382 }
383
384 #[test]
385 fn test_pure_fn_with_extern_system_abi() {
386 let f = PureFn {
387 attrs: vec![],
388 vis: PureVis::Private,
389 is_async: false,
390 is_async_inferred: false,
391 is_const: false,
392 is_unsafe: false,
393 abi: Some("system".to_string()),
394 name: "system_func".to_string(),
395 generics: PureGenerics::default(),
396 params: vec![],
397 ret: None,
398 body: PureBlock::default(),
399 };
400 let syn_fn = f.to_syn().unwrap();
401 let output = syn_fn.to_token_stream().to_string();
402 assert!(
403 output.contains(r#"extern "system""#),
404 r#"Output should contain 'extern "system"': {}"#,
405 output
406 );
407 }
408
409 #[test]
410 fn test_pure_fn_with_extern_rust_abi() {
411 let f = PureFn {
412 attrs: vec![],
413 vis: PureVis::Public,
414 is_async: false,
415 is_async_inferred: false,
416 is_const: false,
417 is_unsafe: false,
418 abi: Some("Rust".to_string()),
419 name: "rust_abi_func".to_string(),
420 generics: PureGenerics::default(),
421 params: vec![],
422 ret: None,
423 body: PureBlock::default(),
424 };
425 let syn_fn = f.to_syn().unwrap();
426 let output = syn_fn.to_token_stream().to_string();
427 assert!(
428 output.contains(r#"extern "Rust""#),
429 r#"Output should contain 'extern "Rust"': {}"#,
430 output
431 );
432 }
433
434 #[test]
435 fn test_pure_fn_no_abi() {
436 let f = PureFn {
437 attrs: vec![],
438 vis: PureVis::Public,
439 is_async: false,
440 is_async_inferred: false,
441 is_const: false,
442 is_unsafe: false,
443 abi: None,
444 name: "normal_func".to_string(),
445 generics: PureGenerics::default(),
446 params: vec![],
447 ret: None,
448 body: PureBlock::default(),
449 };
450 let syn_fn = f.to_syn().unwrap();
451 let output = syn_fn.to_token_stream().to_string();
452 assert!(
453 !output.contains("extern"),
454 "Output should NOT contain 'extern': {}",
455 output
456 );
457 }
458
459 #[test]
460 fn test_pure_fn_unsafe_extern_c() {
461 let f = PureFn {
462 attrs: vec![],
463 vis: PureVis::Public,
464 is_async: false,
465 is_async_inferred: false,
466 is_const: false,
467 is_unsafe: true,
468 abi: Some("C".to_string()),
469 name: "unsafe_c_func".to_string(),
470 generics: PureGenerics::default(),
471 params: vec![],
472 ret: None,
473 body: PureBlock::default(),
474 };
475 let syn_fn = f.to_syn().unwrap();
476 let output = syn_fn.to_token_stream().to_string();
477 assert!(
478 output.contains("unsafe"),
479 "Output should contain 'unsafe': {}",
480 output
481 );
482 assert!(
483 output.contains(r#"extern "C""#),
484 r#"Output should contain 'extern "C"': {}"#,
485 output
486 );
487 }
488}