1use proc_macro::TokenStream;
4use quote::quote;
5use syn::{ punctuated::Punctuated, spanned::Spanned };
6
7#[proc_macro]
9pub fn str(input: TokenStream) -> TokenStream {
10 let Format { expr, args } = syn::parse_macro_input!(input as Format);
11
12 match expr {
13 syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Str(fmt), .. }) => {
15 if args.is_some() {
17 let args = args.unwrap().into_iter();
18
19 quote! { ::std::format!(#fmt, #(#args)*) }
20 }
21 else if fmt.value().contains(&['{', '}'][..]) {
23 quote! { ::std::format!(#fmt) }
24 }
25 else {
27 quote! { #fmt.to_owned() }
28 }
29 },
30
31 _ => quote! { #expr.to_string() }
32 }.into()
33}
34
35struct Format {
37 pub expr: syn::Expr,
38 pub args: Option<Punctuated::<syn::Expr, syn::Token![,]>>,
39}
40
41impl syn::parse::Parse for Format {
42 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
43 let expr = input.parse()?;
44
45 let args = match input.parse::<syn::Token![,]>() {
46 Ok(_) => {
47 if let syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Str(_), .. }) = expr {
48 } else {
49 return Err(syn::Error::new(expr.span(), "Expected the literal string by using format arguments, example: 'str!(\"format str {{}}\", \"arg1\", ..)'"));
50 }
51
52 Some(Punctuated::parse_terminated(input)?)
53 },
54 Err(_) => None
55 };
56
57 Ok(Self { expr, args })
58 }
59}