use crate::Result;
use proc_macro::{Delimiter, Group, Punct, Spacing, Span, TokenTree};
use std::iter::Peekable;
pub fn group_err<T>(span: Span, prefix: &str, expected: Delimiter, hints: &str) -> Result<T>
{
Err((
span,
format!(
"{}\nExpected '{}'.\n{}",
prefix,
match expected
{
Delimiter::Brace => '{',
Delimiter::Bracket => '[',
Delimiter::Parenthesis => '(',
_ => unreachable!("Shouldn't expect None delimiters"),
},
hints
),
))
}
pub fn parse_group(
iter: &mut Peekable<impl Iterator<Item = TokenTree>>,
del: Delimiter,
parent_span: Span,
hints: &str,
) -> Result<Group>
{
let result = peek_parse_group(iter, del, parent_span, hints);
if result.is_ok()
{
let _ = iter.next();
}
result
}
pub fn peek_parse_group(
iter: &mut Peekable<impl Iterator<Item = TokenTree>>,
del: Delimiter,
parent_span: Span,
hints: &str,
) -> Result<Group>
{
if let Some(tree) = iter.peek()
{
if let TokenTree::Group(group) = tree
{
check_delimiter(&group, del)?;
Ok(group.clone())
}
else
{
group_err(tree.span(), "Not a group.", del, hints)
}
}
else
{
group_err(
parent_span,
"Unexpected end of macro invocation.",
del,
hints,
)
}
}
pub fn check_group(tree: TokenTree, del: Delimiter, hints: &str) -> Result<Group>
{
if let TokenTree::Group(group) = tree
{
check_delimiter(&group, del)?;
Ok(group)
}
else
{
group_err(tree.span(), "Not a group.", del, hints)
}
}
pub fn check_delimiter(group: &Group, del: Delimiter) -> Result<()>
{
if group.delimiter() != del
{
group_err(group.span(), "Unexpected delimiter for group.", del, "")
}
else
{
Ok(())
}
}
pub fn punct_is_char(p: &Punct, c: char) -> bool
{
p.as_char() == c && p.spacing() == Spacing::Alone
}
pub fn is_semicolon(p: &Punct) -> bool
{
punct_is_char(p, ';')
}
pub fn is_nested_invocation(p: &Punct) -> bool
{
punct_is_char(p, '#')
}
pub fn next_token(
iter: &mut Peekable<impl Iterator<Item = TokenTree>>,
parent_span: Span,
expected: &str,
) -> Result<TokenTree>
{
let result = peek_next_token(iter, parent_span, expected);
if result.is_ok()
{
let _ = iter.next();
}
result
}
pub fn peek_next_token(
iter: &mut Peekable<impl Iterator<Item = TokenTree>>,
parent_span: Span,
expected: &str,
) -> Result<TokenTree>
{
let make_err = |span, msg| Err((span, format!("{}\nExpected: {}", msg, expected)));
if let Some(token) = iter.peek()
{
match token
{
TokenTree::Group(group) if group.delimiter() == Delimiter::None =>
{
let mut in_group = group.stream().into_iter();
if let Some(result) = in_group.next()
{
match (in_group.next(), in_group.next())
{
(None, _) => Ok(result),
(Some(TokenTree::Punct(ref p)), None) if is_semicolon(&p) => Ok(result),
_ =>
{
make_err(
token.span(),
"Encountered none-delimited group with multiple tokens. This is \
an internal error. Please file a bug report.",
)
},
}
}
else
{
make_err(
token.span(),
"Encountered none-delimited group with no tokens. This is an internal \
error. Please file a bug report.",
)
}
},
token => Ok(token.clone()),
}
}
else
{
make_err(parent_span, "Unexpected end of macro invocation.")
}
}
pub fn extract_argument_list(group: &Group) -> Result<Vec<String>>
{
let mut result = Vec::new();
let mut arg_iter = group.stream().into_iter();
while let Some(token) = arg_iter.next()
{
if let TokenTree::Ident(ident) = token
{
result.push(ident.to_string());
if let Some(token) = arg_iter.next()
{
match &token
{
TokenTree::Punct(punct) if punct_is_char(&punct, ',') => (),
_ => return Err((token.span(), "Expected ','.".into())),
}
}
}
else
{
return Err((
token.span(),
"Expected substitution identifier argument as identifier.".into(),
));
}
}
Ok(result)
}