use proc_macro2::{Ident, TokenStream, TokenTree};
use syn::ItemTrait;
use crate::util::{compile_err, compile_error_str};
pub(crate) fn parse_names_from_tokens(
tokens: &[TokenTree], trait_def: &ItemTrait,
) -> Result<Vec<Ident>, TokenStream> {
if tokens.is_empty() {
return Err(compile_error_str(
"batch-impl: the directive's argument list cannot be empty",
proc_macro2::Span::call_site(),
));
}
parse_name_tokens(tokens, trait_def, "directive arguments")
}
fn parse_name_tokens(
tokens: &[TokenTree], trait_def: &ItemTrait, what: &str,
) -> Result<Vec<Ident>, TokenStream> {
if tokens.is_empty() {
return Err(compile_err!("batch-impl: {} cannot be empty", what));
}
let mut keep = vec![];
let mut exclude = vec![];
let mut prev_was_comma = true; let mut i = 0;
while i < tokens.len() {
match &tokens[i] {
TokenTree::Ident(id) => {
keep.push(Ident::new(&id.to_string(), id.span()));
prev_was_comma = false;
i += 1;
}
TokenTree::Group(g) if g.delimiter() == delimiter![[]] => {
let inner = g.stream().into_iter().collect::<Vec<_>>();
keep.extend(parse_name_tokens(&inner, trait_def, what)?);
prev_was_comma = false;
i += 1;
}
TokenTree::Punct(p) if p.as_char() == ',' => {
if prev_was_comma {
return Err(compile_err!(
"batch-impl: in {}, a comma is in an illegal position \
(no leading/trailing/consecutive commas)",
what
));
}
prev_was_comma = true;
i += 1;
}
TokenTree::Punct(p) if p.as_char() == '-' => {
let (ids, consumed) =
parse_minus_target(&tokens[i + 1..], trait_def, what)?;
exclude.extend(ids);
i += 1 + consumed;
prev_was_comma = false;
}
_ => {
return Err(compile_err!(
"batch-impl: in {}, expected an identifier, comma, `[...]` \
list, or `-` exclusion, got `{}`",
what,
tokens[i]
));
}
}
}
if prev_was_comma {
return Err(compile_err!(
"batch-impl: in {}, a comma is in an illegal position \
(no leading/trailing/consecutive commas)",
what
));
}
let names = keep
.into_iter()
.filter(|id| !exclude.iter().any(|e| e == id))
.collect::<Vec<_>>();
if names.is_empty() {
return Err(compile_err!("batch-impl: {} cannot be empty", what));
}
Ok(names)
}
fn parse_minus_target(
tokens: &[TokenTree], trait_def: &ItemTrait, what: &str,
) -> Result<(Vec<Ident>, usize), TokenStream> {
match tokens.first() {
Some(TokenTree::Ident(id)) => {
Ok((vec![Ident::new(&id.to_string(), id.span())], 1))
}
Some(TokenTree::Group(g)) if g.delimiter() == delimiter![[]] => {
let inner = g.stream().into_iter().collect::<Vec<_>>();
let ids = parse_name_tokens(&inner, trait_def, what)?;
Ok((ids, 1))
}
_ => Err(compile_err!(
"batch-impl: in {}, after `-` expected an identifier or `[...]` \
list (e.g. `-foo`, `-[a,b]`)",
what
)),
}
}
#[derive(Clone, Copy, PartialEq)]
pub(crate) enum ReceiverFilter {
Ref,
Value,
Static,
}
pub(crate) type AllMarkerSpec =
((bool, bool, bool), Option<bool>, Option<ReceiverFilter>);