use proc_macro2::{Delimiter, Ident, Spacing, TokenStream, TokenTree};
use quote::quote;
use crate::parse::parse_item;
use crate::scan::{Cursor, is_punct, scan_stop, scan_with, ScanMode};
use crate::types::*;
pub(crate) fn parse_generic(
tokens: &[TokenTree],
) -> Option<(&[TokenTree], &[TokenTree], &[TokenTree])> {
let mut i = 0usize;
let mut open = None;
while i < tokens.len() {
if is_punct(&tokens[i], '<') {
open = Some(i);
break;
}
if i + 1 < tokens.len()
&& matches!(&tokens[i], TokenTree::Ident(id) if id == "where")
&& matches!(&tokens[i + 1], TokenTree::Group(g) if g.delimiter() == Delimiter::Brace)
{
break;
}
i += 1;
}
let open = open?;
if open == 0 {
return None;
}
let close = matching_angle(tokens, open)?;
Some((
&tokens[..open],
&tokens[open + 1..close],
&tokens[close + 1..],
))
}
pub(crate) fn parse_type_params(
tokens: &[TokenTree],
) -> Option<(&[TokenTree], &[TokenTree])> {
if !matches!(tokens.first(), Some(token) if is_punct(token, '<')) {
return None;
}
let close = matching_angle(tokens, 0)?;
Some((&tokens[1..close], &tokens[close + 1..]))
}
pub(crate) fn matching_angle(tokens: &[TokenTree], open: usize) -> Option<usize> {
let sub = &tokens[open..];
scan_with(sub, &[], ScanMode::Strict).map(|i| i + open)
}
pub(crate) fn is_trait_base(base: &[TokenTree], trait_name: Option<&Ident>) -> bool {
trait_name
.is_some_and(|name| matches!(base.last(), Some(TokenTree::Ident(last)) if last == name))
}
fn split_at_depth0(
tokens: &[TokenTree],
separator: char,
) -> Vec<&[TokenTree]> {
let mut chunks = Vec::new();
let mut rest = tokens;
while let Some(index) = scan_stop(rest, &[separator]) {
chunks.push(&rest[..index]);
rest = &rest[index + 1..];
}
chunks.push(rest);
chunks
}
fn find_colon_at_depth0(tokens: &[TokenTree]) -> Option<usize> {
scan_stop(tokens, &[':']).filter(|&index| {
let TokenTree::Punct(p0)=&tokens[index] else{
return false
};
!(index > 0
&& matches!(&tokens[index-1], TokenTree::Punct(p) if p.as_char() == ':' && p.spacing() == Spacing::Joint)
|| index + 1 < tokens.len()
&& matches!(&tokens[index+1], TokenTree::Punct(p) if p.as_char() == ':' && p0.spacing() == Spacing::Joint))
})
}
pub(crate) fn parse_angle_bracket_contents(
tokens: &[TokenTree],
trait_name: Option<&Ident>,
) -> TyTypeParam {
let mut params = Vec::new();
let mut bindings = Vec::new();
let mut where_clauses = Vec::new();
for chunk in split_at_depth0(tokens, ',') {
if chunk.is_empty() {
continue;
}
if chunk.len() >= 2
&& matches!(&chunk[0], TokenTree::Ident(id) if id == "where")
&& matches!(&chunk[1], TokenTree::Group(g) if g.delimiter() == Delimiter::Brace)
{
let group = match &chunk[1] {
TokenTree::Group(g) => g,
_ => unreachable!(),
};
where_clauses.push(group.stream());
continue;
}
if let Some(eq) = scan_stop(chunk, &['=']) {
bindings.push((
chunk[..eq].iter().cloned().collect(),
chunk[eq + 1..].iter().cloned().collect(),
));
} else if let Some(colon) = find_colon_at_depth0(chunk) {
params.push((
chunk[..colon].iter().cloned().collect(),
Some(
parse_item(
&mut Cursor::new(&chunk[colon + 1..]),
Op::Dash,
trait_name,
)
.unwrap_or_else(empty),
),
));
} else {
params.push((chunk.iter().cloned().collect(), None));
}
}
TyTypeParam {
params,
bindings,
where_clauses,
}
}
pub(crate) fn eat_where_suffix(
rest: &[TokenTree],
) -> (Vec<TokenStream>, &[TokenTree]) {
let mut wheres = Vec::new();
let mut i = 0usize;
while i + 1 < rest.len() {
match (&rest[i], &rest[i + 1]) {
(TokenTree::Ident(id), TokenTree::Group(g))
if id == "where" && g.delimiter() == Delimiter::Brace =>
{
wheres.push(g.stream());
i += 2;
},
_ => break,
}
}
(wheres, &rest[i..])
}
pub(crate) fn primitive(tokens: &[TokenTree]) -> Ty {
Ty::Primitive(TyPrimitive(tokens.iter().cloned().collect()))
}
pub(crate) fn empty() -> Ty {
Ty::Primitive(TyPrimitive(quote![]))
}