use proc_macro2::{Delimiter, Group, TokenStream, TokenTree};
use crate::diagnostic::compile_error_str;
use crate::scan::{Cursor, is_arrow};
pub(crate) fn where_process(
cursor: &mut Cursor,
) -> Result<Vec<TokenTree>, TokenStream> {
let tokens = cursor.take_rest();
let mut result = vec![];
let mut i = 0;
while i < tokens.len() {
if let TokenTree::Ident(ident) = &tokens[i]
&& ident == "where"
&& i + 1 < tokens.len()
&& !matches!(&tokens[i+1],TokenTree::Group(g)
if g.delimiter() == Delimiter::Brace)
{
let Some((where_body, rest_index)) = scan_body_boundary(&tokens[i + 1..])
else {
return Err(compile_error_str(
"batch-impl: `where` 谓词后缺少代码块 {...}",
));
};
result.push(ident.clone().into());
result.push(where_body);
i += 1 + rest_index;
} else if let TokenTree::Group(g) = &tokens[i]
&& g.delimiter() == Delimiter::Bracket
&& !(i > 0
&& matches!(&tokens[i - 1], TokenTree::Punct(p)
if p.as_char() == '!'))
{
let v = g.stream().into_iter().collect::<Vec<_>>();
let vt = where_process(&mut Cursor::new(&v))?;
result.push(
Group::new(Delimiter::Bracket, vt.into_iter().collect()).into(),
);
i += 1
} else {
result.push(tokens[i].clone());
i += 1;
};
}
Ok(result)
}
fn scan_body_boundary(tokens: &[TokenTree]) -> Option<(TokenTree, usize)> {
let mut depth = 0usize;
let mut j = 0;
let mut result = vec![];
while j < tokens.len() {
match &tokens[j] {
TokenTree::Punct(p) if p.as_char() == '<' => {
depth += 1;
result.push(&tokens[j])
}
TokenTree::Punct(p) if p.as_char() == '>' => {
if !is_arrow(tokens, j) {
depth = depth.saturating_sub(1);
}
result.push(&tokens[j])
}
TokenTree::Group(g)
if g.delimiter() == Delimiter::Brace
&& depth == 0
&& !is_macro_body(tokens, j) =>
{
return Some((
Group::new(
Delimiter::Brace,
result.into_iter().cloned().collect(),
)
.into(),
j,
));
}
TokenTree::Ident(w) if w == "where" && depth == 0 => {
return Some((
Group::new(
Delimiter::Brace,
result.into_iter().cloned().collect(),
)
.into(),
j,
));
}
_ => result.push(&tokens[j]),
}
j += 1;
}
None
}
fn is_macro_body(tokens: &[TokenTree], index: usize) -> bool {
index >= 2
&& matches!(&tokens[index - 1], TokenTree::Punct(p) if p.as_char() == '!')
&& matches!(&tokens[index - 2], TokenTree::Ident(_))
}