1use crate::{attribute_list::AttributeList, utility::parse_crate_path};
2use proc_macro::TokenStream;
3use proc_macro2::Ident;
4use quote::quote;
5use std::error::Error;
6use syn::{Expr, ExprLit, Item, ItemFn, ItemStruct, Lit, LitStr};
7
8const RAW_STRING_PREFIX: &str = "r#";
9
10pub fn generate(attributes: &AttributeList, item: &Item) -> Result<TokenStream, Box<dyn Error>> {
11 match item {
12 Item::Fn(function) => generate_function(attributes, function),
13 Item::Struct(r#struct) => generate_struct(attributes, r#struct),
14 _ => Err("only functions and structs can be quoted".into()),
15 }
16}
17
18fn generate_function(
19 attributes: &AttributeList,
20 function: &ItemFn,
21) -> Result<TokenStream, Box<dyn Error>> {
22 let crate_path = parse_crate_path(attributes)?;
23 let ident = &function.sig.ident;
24 let visibility = &function.vis;
25 let quote_name = Ident::new(&get_quote_name(ident, "_fn"), ident.span());
26 let name_string = get_name_string(ident);
27
28 Ok(quote! {
29 #visibility fn #quote_name() -> #crate_path::Fn {
30 #crate_path::Fn::new(#name_string, syn::parse2(quote::quote!(#function)).unwrap())
31 }
32
33 #function
34 }
35 .into())
36}
37
38fn generate_struct(
39 attributes: &AttributeList,
40 r#struct: &ItemStruct,
41) -> Result<TokenStream, Box<dyn Error>> {
42 let crate_path = parse_crate_path(attributes)?;
43 let ident = &r#struct.ident;
44 let visibility = &r#struct.vis;
45 let quote_name = Ident::new(&get_quote_name(ident, "_struct"), ident.span());
46 let name_string = get_name_string(ident);
47
48 Ok(quote! {
49 #visibility fn #quote_name() -> #crate_path::Struct {
50 #crate_path::Struct::new(#name_string, syn::parse2(quote::quote!(#r#struct)).unwrap())
51 }
52
53 #r#struct
54 }
55 .into())
56}
57
58fn get_quote_name(ident: &Ident, suffix: &str) -> String {
59 ident
60 .to_string()
61 .strip_prefix(RAW_STRING_PREFIX)
62 .map(ToOwned::to_owned)
63 .unwrap_or_else(|| ident.to_string())
64 .to_lowercase()
65 + suffix
66}
67
68fn get_name_string(ident: &Ident) -> Expr {
69 Expr::Lit(ExprLit {
70 attrs: Vec::new(),
71 lit: Lit::Str(LitStr::new(&ident.to_string(), ident.span())),
72 })
73}