use proc_macro2::{Delimiter, Span, TokenStream, TokenTree};
use syn::{
LitInt, LitStr,
ext::IdentExt,
parenthesized,
parse::{Parse, ParseBuffer, ParseStream},
};
use super::{Inputs, TStr};
pub(crate) fn parse_inputs(
ts: proc_macro2::TokenStream,
crate_token: &mut Option<proc_macro::TokenStream>,
) -> Result<Inputs, syn::Error> {
syn::parse::Parser::parse2(
|input: ParseStream| {
let content;
parenthesized!(content in input);
let crate_path = content
.parse::<proc_macro2::TokenStream>()
.expect("TokenStream never fails to parse");
*crate_token = Some(crate_path.into());
let expr_tokens = match input.parse::<TokenTree>()? {
TokenTree::Group(group) if group.delimiter() == Delimiter::None => group.stream(),
_ => panic!("not an :expr!"),
};
let string = syn::parse::Parser::parse2(
|ts_input: ParseStream| {
let ret = TStr::parse(ts_input)?;
if !ts_input.is_empty() {
return Err(ts_input.error("unexpected token after the argument"));
}
Ok(ret)
},
expr_tokens,
)?;
Ok(Inputs { string })
},
ts,
)
}
impl Parse for TStr {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
let lookahead = input.lookahead1();
let (string, span) = if lookahead.peek(kw::concat) {
input.parse::<kw::concat>()?;
let (span, content) = parse_post_macro_name(input)?;
let mut value = String::new();
while !content.is_empty() {
let tstr = content.parse::<TStr>()?;
value.push_str(&tstr.string);
if !content.is_empty() {
content.parse::<syn::Token!(,)>()?;
}
}
(value, span)
} else if lookahead.peek(kw::stringify) {
input.parse::<kw::stringify>()?;
let (span, content) = parse_post_macro_name(input)?;
(content.parse::<TokenStream>()?.to_string(), span)
} else if lookahead.peek(syn::Ident::peek_any) {
let ident = input.call(syn::Ident::parse_any)?;
let mut value = ident.to_string();
if value.starts_with("r#") {
value.drain(..2);
}
(value, ident.span())
} else if lookahead.peek(LitStr) {
let lit = input.parse::<LitStr>()?;
(lit.value(), lit.span())
} else if lookahead.peek(LitInt) {
let lit = input.parse::<LitInt>()?;
(lit.base10_digits().to_string(), lit.span())
} else {
return Err(lookahead.error());
};
Ok(Self { string, span })
}
}
fn parse_post_macro_name(input: ParseStream) -> syn::Result<(Span, ParseBuffer)> {
input.parse::<syn::Token!(!)>()?;
let content;
let paren = parenthesized!(content in input);
Ok((paren.span.join(), content))
}
mod kw {
syn::custom_keyword!(concat);
syn::custom_keyword!(stringify);
}