Skip to main content

macro_string/
lib.rs

1//! [![github]](https://github.com/dtolnay/macro-string) [![crates-io]](https://crates.io/crates/macro-string) [![docs-rs]](https://docs.rs/macro-string)
2//!
3//! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
4//! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust
5//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs
6//!
7//! <br>
8//!
9//! This crate is a helper library for procedural macros to perform eager
10//! evaluation of standard library string macros like `concat!` and `env!` in
11//! macro input.
12//!
13//! <table><tr><td>
14//! <b>Supported macros:</b>
15//! <code>concat!</code>,
16//! <code>env!</code>,
17//! <code>include!</code>,
18//! <code>include_str!</code>,
19//! <code>stringify!</code>
20//! </td></tr></table>
21//!
22//! For example, to implement a macro such as the following:
23//!
24//! ```
25//! # macro_rules! include_json {
26//! #     ($path:expr) => { $path };
27//! # }
28//! #
29//! // Parses JSON at compile time and expands to a serde_json::Value.
30//! let j = include_json!(concat!(env!("CARGO_MANIFEST_DIR"), "/manifest.json"));
31//! ```
32//!
33//! the implementation of `include_json!` will need to parse and eagerly
34//! evaluate the two macro calls within its input tokens.
35//!
36//! ```
37//! # extern crate proc_macro;
38//! #
39//! use macro_string::MacroString;
40//! use proc_macro::TokenStream;
41//! use std::fs;
42//! use syn::parse_macro_input;
43//!
44//! # const _: &str = stringify! {
45//! #[proc_macro]
46//! # };
47//! pub fn include_json(input: TokenStream) -> TokenStream {
48//!     let macro_string = parse_macro_input!(input as MacroString);
49//!     let path = match macro_string.eval() {
50//!         Ok(path) => path,
51//!         Err(err) => return TokenStream::from(err.to_compile_error()),
52//!     };
53//!
54//!     let content = match fs::read(&path) {
55//!         Ok(content) => content,
56//!         Err(err) => return TokenStream::from(macro_string.error(err).to_compile_error()),
57//!     };
58//!
59//!     let json: serde_json::Value = match serde_json::from_slice(&content) {
60//!         Ok(json) => json,
61//!         Err(err) => return TokenStream::from(macro_string.error(err).to_compile_error()),
62//!     };
63//!
64//!     /*TODO: print serde_json::Value to TokenStream*/
65//!     # unimplemented!()
66//! }
67//! ```
68
69#![doc(html_root_url = "https://docs.rs/macro-string/0.2.0")]
70
71use proc_macro2::TokenStream;
72use quote::{quote, ToTokens};
73use std::env;
74use std::fmt::Display;
75use std::fs;
76use std::path::{Component, Path, PathBuf};
77use syn::parse::{Error, Parse, ParseBuffer, ParseStream, Parser, Result};
78use syn::punctuated::Punctuated;
79use syn::token::{Brace, Bracket, Paren};
80use syn::{
81    braced, bracketed, parenthesized, Ident, LitBool, LitChar, LitFloat, LitInt, LitStr, Token,
82};
83
84mod kw {
85    #[allow(non_camel_case_types)]
pub struct concat {
    #[allow(dead_code)]
    pub span: ::syn::__private::Span,
}
#[doc(hidden)]
#[allow(dead_code, non_snake_case)]
pub fn concat<__S: ::syn::__private::IntoSpans<::syn::__private::Span>>(span:
        __S) -> concat {
    concat { span: ::syn::__private::IntoSpans::into_spans(span) }
}
const _: () =
    {
        impl ::syn::__private::Default for concat {
            fn default() -> Self {
                concat { span: ::syn::__private::Span::call_site() }
            }
        }
        impl ::syn::__private::CustomToken for concat {
            fn peek(cursor: ::syn::buffer::Cursor) -> ::syn::__private::bool {
                if let ::syn::__private::Some((ident, _rest)) = cursor.ident()
                    {
                    ident == "concat"
                } else { false }
            }
            fn display() -> &'static ::syn::__private::str { "`concat`" }
        }
        impl ::syn::parse::Parse for concat {
            fn parse(input: ::syn::parse::ParseStream)
                -> ::syn::parse::Result<concat> {
                input.step(|cursor|
                        {
                            if let ::syn::__private::Some((ident, rest)) =
                                    cursor.ident() {
                                if ident == "concat" {
                                    return ::syn::__private::Ok((concat { span: ident.span() },
                                                rest));
                                }
                            }
                            ::syn::__private::Err(cursor.error("expected `concat`"))
                        })
            }
        }
        impl ::syn::__private::ToTokens for concat {
            fn to_tokens(&self, tokens: &mut ::syn::__private::TokenStream2) {
                let ident = ::syn::Ident::new("concat", self.span);
                ::syn::__private::TokenStreamExt::append(tokens, ident);
            }
        }
        impl ::syn::__private::Copy for concat {}
        #[allow(clippy :: expl_impl_clone_on_copy)]
        impl ::syn::__private::Clone for concat {
            fn clone(&self) -> Self { *self }
        }
        ;
    };syn::custom_keyword!(concat);
86    #[allow(non_camel_case_types)]
pub struct env {
    #[allow(dead_code)]
    pub span: ::syn::__private::Span,
}
#[doc(hidden)]
#[allow(dead_code, non_snake_case)]
pub fn env<__S: ::syn::__private::IntoSpans<::syn::__private::Span>>(span:
        __S) -> env {
    env { span: ::syn::__private::IntoSpans::into_spans(span) }
}
const _: () =
    {
        impl ::syn::__private::Default for env {
            fn default() -> Self {
                env { span: ::syn::__private::Span::call_site() }
            }
        }
        impl ::syn::__private::CustomToken for env {
            fn peek(cursor: ::syn::buffer::Cursor) -> ::syn::__private::bool {
                if let ::syn::__private::Some((ident, _rest)) = cursor.ident()
                    {
                    ident == "env"
                } else { false }
            }
            fn display() -> &'static ::syn::__private::str { "`env`" }
        }
        impl ::syn::parse::Parse for env {
            fn parse(input: ::syn::parse::ParseStream)
                -> ::syn::parse::Result<env> {
                input.step(|cursor|
                        {
                            if let ::syn::__private::Some((ident, rest)) =
                                    cursor.ident() {
                                if ident == "env" {
                                    return ::syn::__private::Ok((env { span: ident.span() },
                                                rest));
                                }
                            }
                            ::syn::__private::Err(cursor.error("expected `env`"))
                        })
            }
        }
        impl ::syn::__private::ToTokens for env {
            fn to_tokens(&self, tokens: &mut ::syn::__private::TokenStream2) {
                let ident = ::syn::Ident::new("env", self.span);
                ::syn::__private::TokenStreamExt::append(tokens, ident);
            }
        }
        impl ::syn::__private::Copy for env {}
        #[allow(clippy :: expl_impl_clone_on_copy)]
        impl ::syn::__private::Clone for env {
            fn clone(&self) -> Self { *self }
        }
        ;
    };syn::custom_keyword!(env);
87    #[allow(non_camel_case_types)]
pub struct include {
    #[allow(dead_code)]
    pub span: ::syn::__private::Span,
}
#[doc(hidden)]
#[allow(dead_code, non_snake_case)]
pub fn include<__S: ::syn::__private::IntoSpans<::syn::__private::Span>>(span:
        __S) -> include {
    include { span: ::syn::__private::IntoSpans::into_spans(span) }
}
const _: () =
    {
        impl ::syn::__private::Default for include {
            fn default() -> Self {
                include { span: ::syn::__private::Span::call_site() }
            }
        }
        impl ::syn::__private::CustomToken for include {
            fn peek(cursor: ::syn::buffer::Cursor) -> ::syn::__private::bool {
                if let ::syn::__private::Some((ident, _rest)) = cursor.ident()
                    {
                    ident == "include"
                } else { false }
            }
            fn display() -> &'static ::syn::__private::str { "`include`" }
        }
        impl ::syn::parse::Parse for include {
            fn parse(input: ::syn::parse::ParseStream)
                -> ::syn::parse::Result<include> {
                input.step(|cursor|
                        {
                            if let ::syn::__private::Some((ident, rest)) =
                                    cursor.ident() {
                                if ident == "include" {
                                    return ::syn::__private::Ok((include { span: ident.span() },
                                                rest));
                                }
                            }
                            ::syn::__private::Err(cursor.error("expected `include`"))
                        })
            }
        }
        impl ::syn::__private::ToTokens for include {
            fn to_tokens(&self, tokens: &mut ::syn::__private::TokenStream2) {
                let ident = ::syn::Ident::new("include", self.span);
                ::syn::__private::TokenStreamExt::append(tokens, ident);
            }
        }
        impl ::syn::__private::Copy for include {}
        #[allow(clippy :: expl_impl_clone_on_copy)]
        impl ::syn::__private::Clone for include {
            fn clone(&self) -> Self { *self }
        }
        ;
    };syn::custom_keyword!(include);
88    #[allow(non_camel_case_types)]
pub struct include_str {
    #[allow(dead_code)]
    pub span: ::syn::__private::Span,
}
#[doc(hidden)]
#[allow(dead_code, non_snake_case)]
pub fn include_str<__S: ::syn::__private::IntoSpans<::syn::__private::Span>>(span:
        __S) -> include_str {
    include_str { span: ::syn::__private::IntoSpans::into_spans(span) }
}
const _: () =
    {
        impl ::syn::__private::Default for include_str {
            fn default() -> Self {
                include_str { span: ::syn::__private::Span::call_site() }
            }
        }
        impl ::syn::__private::CustomToken for include_str {
            fn peek(cursor: ::syn::buffer::Cursor) -> ::syn::__private::bool {
                if let ::syn::__private::Some((ident, _rest)) = cursor.ident()
                    {
                    ident == "include_str"
                } else { false }
            }
            fn display() -> &'static ::syn::__private::str { "`include_str`" }
        }
        impl ::syn::parse::Parse for include_str {
            fn parse(input: ::syn::parse::ParseStream)
                -> ::syn::parse::Result<include_str> {
                input.step(|cursor|
                        {
                            if let ::syn::__private::Some((ident, rest)) =
                                    cursor.ident() {
                                if ident == "include_str" {
                                    return ::syn::__private::Ok((include_str {
                                                    span: ident.span(),
                                                }, rest));
                                }
                            }
                            ::syn::__private::Err(cursor.error("expected `include_str`"))
                        })
            }
        }
        impl ::syn::__private::ToTokens for include_str {
            fn to_tokens(&self, tokens: &mut ::syn::__private::TokenStream2) {
                let ident = ::syn::Ident::new("include_str", self.span);
                ::syn::__private::TokenStreamExt::append(tokens, ident);
            }
        }
        impl ::syn::__private::Copy for include_str {}
        #[allow(clippy :: expl_impl_clone_on_copy)]
        impl ::syn::__private::Clone for include_str {
            fn clone(&self) -> Self { *self }
        }
        ;
    };syn::custom_keyword!(include_str);
89    #[allow(non_camel_case_types)]
pub struct stringify {
    #[allow(dead_code)]
    pub span: ::syn::__private::Span,
}
#[doc(hidden)]
#[allow(dead_code, non_snake_case)]
pub fn stringify<__S: ::syn::__private::IntoSpans<::syn::__private::Span>>(span:
        __S) -> stringify {
    stringify { span: ::syn::__private::IntoSpans::into_spans(span) }
}
const _: () =
    {
        impl ::syn::__private::Default for stringify {
            fn default() -> Self {
                stringify { span: ::syn::__private::Span::call_site() }
            }
        }
        impl ::syn::__private::CustomToken for stringify {
            fn peek(cursor: ::syn::buffer::Cursor) -> ::syn::__private::bool {
                if let ::syn::__private::Some((ident, _rest)) = cursor.ident()
                    {
                    ident == "stringify"
                } else { false }
            }
            fn display() -> &'static ::syn::__private::str { "`stringify`" }
        }
        impl ::syn::parse::Parse for stringify {
            fn parse(input: ::syn::parse::ParseStream)
                -> ::syn::parse::Result<stringify> {
                input.step(|cursor|
                        {
                            if let ::syn::__private::Some((ident, rest)) =
                                    cursor.ident() {
                                if ident == "stringify" {
                                    return ::syn::__private::Ok((stringify {
                                                    span: ident.span(),
                                                }, rest));
                                }
                            }
                            ::syn::__private::Err(cursor.error("expected `stringify`"))
                        })
            }
        }
        impl ::syn::__private::ToTokens for stringify {
            fn to_tokens(&self, tokens: &mut ::syn::__private::TokenStream2) {
                let ident = ::syn::Ident::new("stringify", self.span);
                ::syn::__private::TokenStreamExt::append(tokens, ident);
            }
        }
        impl ::syn::__private::Copy for stringify {}
        #[allow(clippy :: expl_impl_clone_on_copy)]
        impl ::syn::__private::Clone for stringify {
            fn clone(&self) -> Self { *self }
        }
        ;
    };syn::custom_keyword!(stringify);
90}
91
92#[derive(#[automatically_derived]
impl ::core::clone::Clone for MacroString {
    #[inline]
    fn clone(&self) -> MacroString {
        MacroString { expr: ::core::clone::Clone::clone(&self.expr) }
    }
}Clone)]
93pub struct MacroString {
94    expr: Expr,
95}
96
97impl Parse for MacroString {
98    fn parse(input: ParseStream) -> Result<Self> {
99        Ok(MacroString {
100            expr: input.call(Expr::parse_strict)?,
101        })
102    }
103}
104
105impl MacroString {
106    /// Evaluate expression to string.
107    ///
108    /// # Errors
109    ///
110    /// Returns an error if the expression was syntactically valid (parsed
111    /// successfully) but cannot be evaluated, for example because it refers to
112    /// an `env!` for which the environment variable is not set, or an
113    /// `include!` for which the file is not found.
114    pub fn eval(&self) -> Result<String> {
115        self.expr.eval()
116    }
117
118    /// Construct a compile error with a Span encompassing the macro string's
119    /// original expression.
120    ///
121    /// ```compile_fail
122    /// my_macro! {
123    ///     resources = concat!(env!("CARGO_MANIFEST_DIR"), "/resources.json")
124    /// }
125    /// ```
126    ///
127    /// ```text
128    /// error: No such file or directory (os error 2) - /path/to/manifest-dir/resources.json
129    ///   --> example.rs:85:17
130    ///    |
131    /// 85 |     resources = concat!(env!("CARGO_MANIFEST_DIR"), "/resources.json")
132    ///    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
133    /// ```
134    pub fn error<T: Display>(&self, message: T) -> syn::Error {
135        syn::Error::new_spanned(&self.expr, message)
136    }
137}
138
139#[derive(#[automatically_derived]
impl ::core::clone::Clone for Expr {
    #[inline]
    fn clone(&self) -> Expr {
        match self {
            Expr::LitStr(__self_0) =>
                Expr::LitStr(::core::clone::Clone::clone(__self_0)),
            Expr::LitChar(__self_0) =>
                Expr::LitChar(::core::clone::Clone::clone(__self_0)),
            Expr::LitInt(__self_0) =>
                Expr::LitInt(::core::clone::Clone::clone(__self_0)),
            Expr::LitFloat(__self_0) =>
                Expr::LitFloat(::core::clone::Clone::clone(__self_0)),
            Expr::LitBool(__self_0) =>
                Expr::LitBool(::core::clone::Clone::clone(__self_0)),
            Expr::Concat(__self_0) =>
                Expr::Concat(::core::clone::Clone::clone(__self_0)),
            Expr::Env(__self_0) =>
                Expr::Env(::core::clone::Clone::clone(__self_0)),
            Expr::Include(__self_0) =>
                Expr::Include(::core::clone::Clone::clone(__self_0)),
            Expr::IncludeStr(__self_0) =>
                Expr::IncludeStr(::core::clone::Clone::clone(__self_0)),
            Expr::Stringify(__self_0) =>
                Expr::Stringify(::core::clone::Clone::clone(__self_0)),
        }
    }
}Clone)]
140enum Expr {
141    LitStr(LitStr),
142    LitChar(LitChar),
143    LitInt(LitInt),
144    LitFloat(LitFloat),
145    LitBool(LitBool),
146    Concat(Concat),
147    Env(Env),
148    Include(Include),
149    IncludeStr(IncludeStr),
150    Stringify(Stringify),
151}
152
153impl Expr {
154    fn eval(&self) -> Result<String> {
155        match self {
156            Expr::LitStr(lit) => Ok(lit.value()),
157            Expr::LitChar(lit) => Ok(lit.value().to_string()),
158            Expr::LitInt(lit) => Ok(lit.base10_digits().to_owned()),
159            Expr::LitFloat(lit) => Ok(lit.base10_digits().to_owned()),
160            Expr::LitBool(lit) => Ok(lit.value.to_string()),
161            Expr::Concat(expr) => {
162                let mut concat = String::new();
163                for arg in &expr.args {
164                    concat += &arg.eval()?;
165                }
166                Ok(concat)
167            }
168            Expr::Env(expr) => {
169                let key = expr.arg.eval()?;
170                match env::var(&key) {
171                    Ok(value) => Ok(value),
172                    Err(err) => Err(Error::new_spanned(expr, err)),
173                }
174            }
175            Expr::Include(expr) => {
176                let path = expr.arg.eval()?;
177                let content = fs_read(&expr, &path)?;
178                let inner = Expr::parse_strict.parse_str(&content)?;
179                inner.eval()
180            }
181            Expr::IncludeStr(expr) => {
182                let path = expr.arg.eval()?;
183                fs_read(&expr, &path)
184            }
185            Expr::Stringify(expr) => Ok(expr.tokens.to_string()),
186        }
187    }
188}
189
190fn fs_read(span: &dyn ToTokens, path: impl AsRef<Path>) -> Result<String> {
191    let mut path = path.as_ref();
192    if path.is_relative() {
193        let name = span.to_token_stream().into_iter().next().unwrap();
194        return Err(Error::new_spanned(
195            span,
196            ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("a relative path is not supported here; use `{0}!(concat!(env!(\"CARGO_MANIFEST_DIR\"), ...))`",
                name))
    })format!("a relative path is not supported here; use `{name}!(concat!(env!(\"CARGO_MANIFEST_DIR\"), ...))`"),
197        ));
198    }
199
200    // Make Windows verbatim paths work even with mixed path separators, which
201    // can happen when a path is produced using `concat!`.
202    let path_buf: PathBuf;
203    if let Some(Component::Prefix(prefix)) = path.components().next() {
204        if prefix.kind().is_verbatim() {
205            path_buf = path.components().collect();
206            path = &path_buf;
207        }
208    }
209
210    match fs::read_to_string(path) {
211        Ok(content) => Ok(content),
212        Err(err) => Err(Error::new_spanned(
213            span,
214            ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0} {1}", err, path.display()))
    })format!("{} {}", err, path.display()),
215        )),
216    }
217}
218
219#[derive(#[automatically_derived]
impl ::core::clone::Clone for Concat {
    #[inline]
    fn clone(&self) -> Concat {
        Concat {
            name: ::core::clone::Clone::clone(&self.name),
            bang_token: ::core::clone::Clone::clone(&self.bang_token),
            delimiter: ::core::clone::Clone::clone(&self.delimiter),
            args: ::core::clone::Clone::clone(&self.args),
        }
    }
}Clone)]
220struct Concat {
221    name: kw::concat,
222    bang_token: ::syn::token::NotToken![!],
223    delimiter: MacroDelimiter,
224    args: Punctuated<Expr, ::syn::token::CommaToken![,]>,
225}
226
227#[derive(#[automatically_derived]
impl ::core::clone::Clone for Env {
    #[inline]
    fn clone(&self) -> Env {
        Env {
            name: ::core::clone::Clone::clone(&self.name),
            bang_token: ::core::clone::Clone::clone(&self.bang_token),
            delimiter: ::core::clone::Clone::clone(&self.delimiter),
            arg: ::core::clone::Clone::clone(&self.arg),
            trailing_comma: ::core::clone::Clone::clone(&self.trailing_comma),
        }
    }
}Clone)]
228struct Env {
229    name: kw::env,
230    bang_token: ::syn::token::NotToken![!],
231    delimiter: MacroDelimiter,
232    arg: Box<Expr>,
233    trailing_comma: Option<::syn::token::CommaToken![,]>,
234}
235
236#[derive(#[automatically_derived]
impl ::core::clone::Clone for Include {
    #[inline]
    fn clone(&self) -> Include {
        Include {
            name: ::core::clone::Clone::clone(&self.name),
            bang_token: ::core::clone::Clone::clone(&self.bang_token),
            delimiter: ::core::clone::Clone::clone(&self.delimiter),
            arg: ::core::clone::Clone::clone(&self.arg),
            trailing_comma: ::core::clone::Clone::clone(&self.trailing_comma),
        }
    }
}Clone)]
237struct Include {
238    name: kw::include,
239    bang_token: ::syn::token::NotToken![!],
240    delimiter: MacroDelimiter,
241    arg: Box<Expr>,
242    trailing_comma: Option<::syn::token::CommaToken![,]>,
243}
244
245#[derive(#[automatically_derived]
impl ::core::clone::Clone for IncludeStr {
    #[inline]
    fn clone(&self) -> IncludeStr {
        IncludeStr {
            name: ::core::clone::Clone::clone(&self.name),
            bang_token: ::core::clone::Clone::clone(&self.bang_token),
            delimiter: ::core::clone::Clone::clone(&self.delimiter),
            arg: ::core::clone::Clone::clone(&self.arg),
            trailing_comma: ::core::clone::Clone::clone(&self.trailing_comma),
        }
    }
}Clone)]
246struct IncludeStr {
247    name: kw::include_str,
248    bang_token: ::syn::token::NotToken![!],
249    delimiter: MacroDelimiter,
250    arg: Box<Expr>,
251    trailing_comma: Option<::syn::token::CommaToken![,]>,
252}
253
254#[derive(#[automatically_derived]
impl ::core::clone::Clone for Stringify {
    #[inline]
    fn clone(&self) -> Stringify {
        Stringify {
            name: ::core::clone::Clone::clone(&self.name),
            bang_token: ::core::clone::Clone::clone(&self.bang_token),
            delimiter: ::core::clone::Clone::clone(&self.delimiter),
            tokens: ::core::clone::Clone::clone(&self.tokens),
        }
    }
}Clone)]
255struct Stringify {
256    name: kw::stringify,
257    bang_token: ::syn::token::NotToken![!],
258    delimiter: MacroDelimiter,
259    tokens: TokenStream,
260}
261
262#[derive(#[automatically_derived]
impl ::core::clone::Clone for MacroDelimiter {
    #[inline]
    fn clone(&self) -> MacroDelimiter {
        match self {
            MacroDelimiter::Paren(__self_0) =>
                MacroDelimiter::Paren(::core::clone::Clone::clone(__self_0)),
            MacroDelimiter::Brace(__self_0) =>
                MacroDelimiter::Brace(::core::clone::Clone::clone(__self_0)),
            MacroDelimiter::Bracket(__self_0) =>
                MacroDelimiter::Bracket(::core::clone::Clone::clone(__self_0)),
        }
    }
}Clone)]
263enum MacroDelimiter {
264    Paren(Paren),
265    Brace(Brace),
266    Bracket(Bracket),
267}
268
269impl Expr {
270    fn parse_strict(input: ParseStream) -> Result<Self> {
271        Self::parse(input, false)
272    }
273
274    fn parse_any(input: ParseStream) -> Result<Self> {
275        Self::parse(input, true)
276    }
277
278    fn parse(input: ParseStream, allow_nonstring_literals: bool) -> Result<Self> {
279        let lookahead = input.lookahead1();
280        if lookahead.peek(LitStr) {
281            let lit: LitStr = input.parse()?;
282            if !lit.suffix().is_empty() {
283                return Err(Error::new(
284                    lit.span(),
285                    "unexpected suffix on string literal",
286                ));
287            }
288            Ok(Expr::LitStr(lit))
289        } else if allow_nonstring_literals && input.peek(LitChar) {
290            let lit: LitChar = input.parse()?;
291            if !lit.suffix().is_empty() {
292                return Err(Error::new(lit.span(), "unexpected suffix on char literal"));
293            }
294            Ok(Expr::LitChar(lit))
295        } else if allow_nonstring_literals && input.peek(LitInt) {
296            let lit: LitInt = input.parse()?;
297            match lit.suffix() {
298                "" | "i8" | "i16" | "i32" | "i64" | "i128" | "u8" | "u16" | "u32" | "u64"
299                | "u128" | "f16" | "f32" | "f64" | "f128" => {}
300                _ => {
301                    return Err(Error::new(
302                        lit.span(),
303                        "unexpected suffix on integer literal",
304                    ));
305                }
306            }
307            Ok(Expr::LitInt(lit))
308        } else if allow_nonstring_literals && input.peek(LitFloat) {
309            let lit: LitFloat = input.parse()?;
310            match lit.suffix() {
311                "" | "f16" | "f32" | "f64" | "f128" => {}
312                _ => return Err(Error::new(lit.span(), "unexpected suffix on float literal")),
313            }
314            Ok(Expr::LitFloat(lit))
315        } else if allow_nonstring_literals && input.peek(LitBool) {
316            input.parse().map(Expr::LitBool)
317        } else if lookahead.peek(kw::concat) {
318            input.parse().map(Expr::Concat)
319        } else if lookahead.peek(kw::env) {
320            input.parse().map(Expr::Env)
321        } else if lookahead.peek(kw::include) {
322            input.parse().map(Expr::Include)
323        } else if lookahead.peek(kw::include_str) {
324            input.parse().map(Expr::IncludeStr)
325        } else if lookahead.peek(kw::stringify) {
326            input.parse().map(Expr::Stringify)
327        } else if input.peek(Ident) && input.peek2(::syn::token::NotToken![!]) && input.peek3(Paren) {
328            let ident: Ident = input.parse()?;
329            let bang_token: ::syn::token::NotToken![!] = input.parse()?;
330            let unsupported = {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&ident, &mut _s);
    ::quote::ToTokens::to_tokens(&bang_token, &mut _s);
    _s
}quote!(#ident #bang_token);
331            Err(Error::new_spanned(
332                unsupported,
333                "unsupported macro, expected one of: `concat!`, `env!`, `include!`, `include_str!`, `stringify!`",
334            ))
335        } else {
336            Err(lookahead.error())
337        }
338    }
339}
340
341impl ToTokens for Expr {
342    fn to_tokens(&self, tokens: &mut TokenStream) {
343        match self {
344            Expr::LitStr(expr) => expr.to_tokens(tokens),
345            Expr::LitChar(expr) => expr.to_tokens(tokens),
346            Expr::LitInt(expr) => expr.to_tokens(tokens),
347            Expr::LitFloat(expr) => expr.to_tokens(tokens),
348            Expr::LitBool(expr) => expr.to_tokens(tokens),
349            Expr::Concat(expr) => expr.to_tokens(tokens),
350            Expr::Env(expr) => expr.to_tokens(tokens),
351            Expr::Include(expr) => expr.to_tokens(tokens),
352            Expr::IncludeStr(expr) => expr.to_tokens(tokens),
353            Expr::Stringify(expr) => expr.to_tokens(tokens),
354        }
355    }
356}
357
358macro_rules! macro_delimiter {
359    ($var:ident in $input:ident) => {{
360        let (delim, content) = $input.call(macro_delimiter)?;
361        $var = content;
362        delim
363    }};
364}
365
366fn macro_delimiter(input: ParseStream) -> Result<(MacroDelimiter, ParseBuffer)> {
367    let content;
368    let lookahead = input.lookahead1();
369    let delim = if input.peek(Paren) {
370        MacroDelimiter::Paren(match ::syn::__private::parse_parens(&input) {
    ::syn::__private::Ok(parens) => {
        content = parens.content;
        _ = content;
        parens.token
    }
    ::syn::__private::Err(error) => { return ::syn::__private::Err(error); }
}parenthesized!(content in input))
371    } else if input.peek(Brace) {
372        MacroDelimiter::Brace(match ::syn::__private::parse_braces(&input) {
    ::syn::__private::Ok(braces) => {
        content = braces.content;
        _ = content;
        braces.token
    }
    ::syn::__private::Err(error) => { return ::syn::__private::Err(error); }
}braced!(content in input))
373    } else if input.peek(Bracket) {
374        MacroDelimiter::Bracket(match ::syn::__private::parse_brackets(&input) {
    ::syn::__private::Ok(brackets) => {
        content = brackets.content;
        _ = content;
        brackets.token
    }
    ::syn::__private::Err(error) => { return ::syn::__private::Err(error); }
}bracketed!(content in input))
375    } else {
376        return Err(lookahead.error());
377    };
378    Ok((delim, content))
379}
380
381impl MacroDelimiter {
382    fn surround<F>(&self, tokens: &mut TokenStream, f: F)
383    where
384        F: FnOnce(&mut TokenStream),
385    {
386        match self {
387            MacroDelimiter::Paren(delimiter) => delimiter.surround(tokens, f),
388            MacroDelimiter::Brace(delimiter) => delimiter.surround(tokens, f),
389            MacroDelimiter::Bracket(delimiter) => delimiter.surround(tokens, f),
390        }
391    }
392}
393
394impl Parse for Concat {
395    fn parse(input: ParseStream) -> Result<Self> {
396        let content;
397        Ok(Concat {
398            name: input.parse()?,
399            bang_token: input.parse()?,
400            delimiter: {
    let (delim, content) = input.call(macro_delimiter)?;
    content = content;
    delim
}macro_delimiter!(content in input),
401            args: Punctuated::parse_terminated_with(&content, Expr::parse_any)?,
402        })
403    }
404}
405
406impl ToTokens for Concat {
407    fn to_tokens(&self, tokens: &mut TokenStream) {
408        self.name.to_tokens(tokens);
409        self.bang_token.to_tokens(tokens);
410        self.delimiter
411            .surround(tokens, |tokens| self.args.to_tokens(tokens));
412    }
413}
414
415impl Parse for Env {
416    fn parse(input: ParseStream) -> Result<Self> {
417        let content;
418        Ok(Env {
419            name: input.parse()?,
420            bang_token: input.parse()?,
421            delimiter: {
    let (delim, content) = input.call(macro_delimiter)?;
    content = content;
    delim
}macro_delimiter!(content in input),
422            arg: Expr::parse_strict(&content).map(Box::new)?,
423            trailing_comma: content.parse()?,
424        })
425    }
426}
427
428impl ToTokens for Env {
429    fn to_tokens(&self, tokens: &mut TokenStream) {
430        self.name.to_tokens(tokens);
431        self.bang_token.to_tokens(tokens);
432        self.delimiter.surround(tokens, |tokens| {
433            self.arg.to_tokens(tokens);
434            self.trailing_comma.to_tokens(tokens);
435        });
436    }
437}
438
439impl Parse for Include {
440    fn parse(input: ParseStream) -> Result<Self> {
441        let content;
442        Ok(Include {
443            name: input.parse()?,
444            bang_token: input.parse()?,
445            delimiter: {
    let (delim, content) = input.call(macro_delimiter)?;
    content = content;
    delim
}macro_delimiter!(content in input),
446            arg: Expr::parse_strict(&content).map(Box::new)?,
447            trailing_comma: content.parse()?,
448        })
449    }
450}
451
452impl ToTokens for Include {
453    fn to_tokens(&self, tokens: &mut TokenStream) {
454        self.name.to_tokens(tokens);
455        self.bang_token.to_tokens(tokens);
456        self.delimiter.surround(tokens, |tokens| {
457            self.arg.to_tokens(tokens);
458            self.trailing_comma.to_tokens(tokens);
459        });
460    }
461}
462
463impl Parse for IncludeStr {
464    fn parse(input: ParseStream) -> Result<Self> {
465        let content;
466        Ok(IncludeStr {
467            name: input.parse()?,
468            bang_token: input.parse()?,
469            delimiter: {
    let (delim, content) = input.call(macro_delimiter)?;
    content = content;
    delim
}macro_delimiter!(content in input),
470            arg: Expr::parse_strict(&content).map(Box::new)?,
471            trailing_comma: content.parse()?,
472        })
473    }
474}
475
476impl ToTokens for IncludeStr {
477    fn to_tokens(&self, tokens: &mut TokenStream) {
478        self.name.to_tokens(tokens);
479        self.bang_token.to_tokens(tokens);
480        self.delimiter.surround(tokens, |tokens| {
481            self.arg.to_tokens(tokens);
482            self.trailing_comma.to_tokens(tokens);
483        });
484    }
485}
486
487impl Parse for Stringify {
488    fn parse(input: ParseStream) -> Result<Self> {
489        let content;
490        Ok(Stringify {
491            name: input.parse()?,
492            bang_token: input.parse()?,
493            delimiter: {
    let (delim, content) = input.call(macro_delimiter)?;
    content = content;
    delim
}macro_delimiter!(content in input),
494            tokens: content.parse()?,
495        })
496    }
497}
498
499impl ToTokens for Stringify {
500    fn to_tokens(&self, tokens: &mut TokenStream) {
501        self.name.to_tokens(tokens);
502        self.bang_token.to_tokens(tokens);
503        self.delimiter
504            .surround(tokens, |tokens| self.tokens.to_tokens(tokens));
505    }
506}