use crate::body::pull_interpolation;
use proc_macro::{Delimiter, Ident, TokenStream, TokenTree};
use proc_macro2::{Ident as Ident2, TokenStream as TokenStream2};
use quote::quote;
use std::iter::Peekable;
pub struct Tags {
pub is_async: bool,
pub vis: TokenStream2,
pub fn_name: TokenStream2,
}
pub fn parse_intro(toks: &mut impl Iterator<Item = TokenTree>) -> Tags {
let mut is_async = false;
let mut vis = quote!(::mir::Visibility::Private);
let mut captured = vec![];
let fn_name = loop {
let next = toks
.next()
.expect("Unexpectedly reached end of token stream in function! macro");
match next {
TokenTree::Ident(ident) if ident.to_string() == "async" => {
is_async = true;
}
TokenTree::Ident(ident) if ident.to_string() == "pub" => {
vis = quote!(::mir::Visibility::Public);
}
TokenTree::Ident(ident) => {
break ident.to_string();
}
TokenTree::Punct(punct) if punct.as_char() == '#' => {
break pull_interpolation(toks, &mut captured, false);
}
_ => panic!(
"Expected one of: async, pub, or the function's name. Got: {:?}",
next
),
}
};
let fn_name = if captured.is_empty() {
quote!( ::mir::Ident(#fn_name.to_string()) )
} else {
let captured = captured
.into_iter()
.map(|name| Ident2::new(&name, proc_macro2::Span::call_site()));
quote!(::mir::Ident(format!("{}", #( #captured ),*)))
};
Tags {
is_async,
vis,
fn_name,
}
}
pub struct Arg {
pub name: String,
pub arg_type: TokenStream2,
pub default: TokenStream2,
}
pub fn parse_args(arg_toks: TokenStream) -> Vec<Arg> {
let mut args = Vec::new();
let mut arg_toks = arg_toks.into_iter().peekable();
loop {
let arg_name = match arg_toks.next() {
Some(TokenTree::Ident(ident)) => ident.to_string(),
Some(other) => panic!("Expected an argument name. Got: {:?}", other),
None => break,
};
match arg_toks.next() {
Some(TokenTree::Punct(punct)) if punct.as_char() == ':' => (),
Some(other) => panic!("Expected a colon. Got: {:?}", other),
None => panic!("Expected a colon. Got: end of token stream"),
}
let arg_type = match arg_toks.next() {
Some(TokenTree::Ident(ident)) => parse_type(ident, &mut arg_toks),
Some(TokenTree::Punct(punct)) if punct.as_char() == '#' => {
let mut captured = vec![];
let placeholder = pull_interpolation(&mut arg_toks, &mut captured, false);
let captured = captured
.into_iter()
.map(|name| Ident2::new(&name, proc_macro2::Span::call_site()));
quote! {
format!(#placeholder, #( #captured ),*)
}
}
other => panic!("Expected an argument type. Got: {:?}", other),
};
let default = match arg_toks.peek() {
Some(TokenTree::Punct(punct)) if punct.as_char() == '=' => {
arg_toks.next(); match arg_toks.next() {
Some(TokenTree::Literal(lit)) => {
let lit = lit.to_string();
quote!(Some(#lit.to_string()))
}
Some(other) => panic!("Expected a default value. Got: {:?}", other),
None => panic!("Expected a default value. Got: end of token stream"),
}
}
Some(TokenTree::Punct(punct)) if [',', ')'].contains(&punct.as_char()) => {
quote!(None)
}
Some(other) => panic!("Expected one of: , or ) or =. Got: {:?}", other),
None => quote!(None),
};
args.push(Arg {
name: arg_name,
arg_type,
default,
});
match arg_toks.next() {
Some(TokenTree::Punct(punct)) if punct.as_char() == ',' => (),
None => break,
Some(other) => panic!(
"Expected a comma or a closing parenthesis. Got: {:?}",
other
),
}
}
args
}
pub fn parse_type(
ident: Ident,
toks: &mut Peekable<impl Iterator<Item = TokenTree>>,
) -> TokenStream2 {
let mut ident = ident.to_string();
while matches!(toks.peek(), Some(TokenTree::Punct(punct)) if punct.as_char() == '.') {
ident += &toks.next().unwrap().to_string();
if !matches!(toks.peek(), Some(TokenTree::Ident(_))) {
panic!("Expected an identifier after a dot. Got: {:?}", toks.peek());
}
ident += &toks.next().unwrap().to_string();
}
if matches!(toks.peek(), Some(TokenTree::Group(group))
if matches!(group.delimiter(), Delimiter::Bracket))
{
ident += &toks.next().unwrap().to_string();
}
quote! {
#ident.to_string()
}
}
pub fn parse_return(toks: &mut Peekable<impl Iterator<Item = TokenTree>>) -> TokenStream2 {
loop {
match toks.peek() {
Some(TokenTree::Punct(punct)) if punct.as_char() == '-' => {
toks.next();
match toks.next() {
Some(TokenTree::Punct(punct)) if punct.as_char() == '>' => match toks.next() {
Some(TokenTree::Ident(ident)) => {
break parse_type(ident, toks);
}
Some(TokenTree::Punct(punct)) if punct.as_char() == '#' => {
let mut captured = vec![];
let placeholder = pull_interpolation(toks, &mut captured, false);
let captured = captured
.into_iter()
.map(|name| Ident2::new(&name, proc_macro2::Span::call_site()));
break quote!(format!(#placeholder, #( #captured ),*));
}
next => panic!("Expected the return type. Got: {:?}", next),
},
next => panic!("Expected ->. Got: {:?}", next),
}
}
Some(TokenTree::Group(group)) if group.delimiter() == Delimiter::Brace => {
break quote!("".to_string());
}
next => panic!("Expected -> or {{. Got: {:?}", next),
}
}
}