1#![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/README.md"))]
2
3use proc_macro::TokenStream;
4use proc_macro2::TokenStream as TokenStream2;
5use quote::quote;
6
7#[proc_macro]
9pub fn str(input: TokenStream) -> TokenStream {
10 if input.is_empty() {
12 return quote! { ::std::string::String::new() }.into();
13 }
14
15 let Format { expr, args } = syn::parse_macro_input!(input as Format);
17
18 match expr {
19 syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Str(fmt), .. }) => {
21 if args.is_some() {
23 quote! { ::std::format!(#fmt #args) }
24 }
25 else if fmt.value().contains(&['{', '}'][..]) {
27 quote! { ::std::format!(#fmt) }
28 }
29 else {
31 quote! { #fmt.to_owned() }
32 }
33 },
34
35 _ => quote! { #expr.to_string() }
37 }.into()
38}
39
40struct Format {
42 pub expr: syn::Expr,
43 pub args: Option<TokenStream2>,
44}
45
46impl syn::parse::Parse for Format {
47 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
48 let expr = input.parse()?;
50
51 let args = if input.peek(syn::token::Comma) {
53 Some(input.parse()?)
54 } else {
55 None
56 };
57
58 Ok(Self { expr, args })
59 }
60}