use proc_macro2::{Span, TokenStream as TokenStream2, TokenTree};
use quote::quote;
use crate::core::types::{ImplSpec, SlotKind, ParseResult, err};
use crate::core::recursion::{RecursionGuard, span_suffix};
use crate::core::utils::*;
use crate::core::caret::expand_caret;
pub fn split_by_dash(tokens: &[TokenTree]) -> Option<(Vec<TokenTree>, Vec<TokenTree>)> {
let mut depth = 0u32;
let mut last = None;
for (i, tt) in tokens.iter().enumerate() {
if is_punct(tt, '-') && depth == 0 {
let is_arrow = i + 1 < tokens.len() && is_punct(&tokens[i + 1], '>');
if !is_arrow {
last = Some(i);
}
}
if is_punct(tt, '<') {
depth += 1;
} else if is_punct(tt, '>') {
depth = depth.saturating_sub(1);
}
}
last.map(|i| (tokens[..i].to_vec(), tokens[i + 1..].to_vec()))
}
pub fn expand_dash(
tokens: TokenStream2,
parent_types: &[TokenStream2],
parent_trait: &Option<Vec<TokenStream2>>,
parent_assoc: &[(TokenStream2, TokenStream2)],
body: Option<TokenStream2>,
span: Span,
) -> ParseResult {
let _guard = match RecursionGuard::new() {
Ok(g) => g,
Err(e) => return e,
};
let tv: Vec<TokenTree> = tokens.clone().into_iter().collect();
if let Some((left, right)) = split_by_dash(&tv) {
let suffix = left.first().map(|t| span_suffix(t.span())).unwrap_or(0);
let left_specs = match expand_dash(
left.into_iter().collect(),
parent_types,
parent_trait,
parent_assoc,
body.clone(),
span,
) {
ParseResult::Ok(s) => s,
ParseResult::Err(e) => return ParseResult::Err(e),
};
let right_ts: TokenStream2 = right.into_iter().collect();
let right_slots = match dash_parse_slots(&right_ts, span) {
Ok(s) => s,
Err(e) => return e,
};
let mut specs = Vec::new();
for spec in &left_specs {
for slot in &right_slots {
let (new_target, extra_types) = dash_append(&spec.target, slot, suffix);
let mut all_types = spec.type_params.clone();
all_types.extend(extra_types);
specs.push(ImplSpec::new(
all_types,
spec.trait_params.clone(),
spec.assoc_bindings.clone(),
new_target,
spec.custom_body.clone(),
));
}
}
ParseResult::Ok(specs)
} else {
dash_parse_start(tokens, parent_types, parent_trait, parent_assoc, span)
}
}
fn dash_parse_slots(ts: &TokenStream2, span: Span) -> Result<Vec<SlotKind>, ParseResult> {
let tv: Vec<TokenTree> = ts.clone().into_iter().collect();
if tv.is_empty() {
return Err(ParseResult::Err(err(
span,
"- 右侧为空,期望类型或类型列表,如 -T 或 -[A,B]",
)));
}
if tv.len() == 1 {
if let TokenTree::Group(ref g) = tv[0] {
if g.delimiter() == proc_macro2::Delimiter::Bracket {
let inner: TokenStream2 = g.stream();
if has_top_level_char(&inner, ',') {
let (segs, _) = split_raw(inner, ',');
return Ok(segs
.into_iter()
.map(|seg| {
let seg_ts: TokenStream2 = seg.into_iter().collect();
if has_top_level_char(&seg_ts, '^') {
let expanded = expand_caret(
seg_ts.clone(),
&[],
&None,
&[],
None,
span,
);
match expanded {
ParseResult::Ok(specs) if !specs.is_empty() => {
SlotKind::Fixed(specs[0].target.clone())
}
_ => SlotKind::Fixed(seg_ts),
}
} else {
SlotKind::Fixed(seg_ts)
}
})
.collect());
}
let inner_ts: TokenStream2 = g.stream();
if has_top_level_char(&inner_ts, '^') {
let expanded = expand_caret(
inner_ts,
&[],
&None,
&[],
None,
span,
);
match expanded {
ParseResult::Ok(specs) if !specs.is_empty() => {
return Ok(vec![SlotKind::Fixed(specs[0].target.clone())]);
}
_ => {}
}
}
return Ok(vec![SlotKind::Fixed(ts.clone())]);
}
return Ok(vec![SlotKind::Fixed(ts.clone())]);
}
}
if has_top_level_char(ts, '^') {
let expanded = expand_caret(
ts.clone(),
&[],
&None,
&[],
None,
span,
);
match expanded {
ParseResult::Ok(specs) if !specs.is_empty() => {
return Ok(vec![SlotKind::Fixed(specs[0].target.clone())]);
}
_ => {}
}
}
Ok(vec![SlotKind::Fixed(ts.clone())])
}
fn dash_parse_start(
tokens: TokenStream2,
parent_types: &[TokenStream2],
parent_trait: &Option<Vec<TokenStream2>>,
parent_assoc: &[(TokenStream2, TokenStream2)],
_span: Span,
) -> ParseResult {
let tv: Vec<TokenTree> = tokens.clone().into_iter().collect();
if tv.len() == 1 {
if let TokenTree::Group(ref g) = tv[0] {
if g.delimiter() == proc_macro2::Delimiter::Parenthesis {
let inner: Vec<TokenTree> = g.stream().into_iter().collect();
if inner.is_empty() {
return ParseResult::Ok(vec![ImplSpec::new(
parent_types.to_vec(),
parent_trait.clone(),
parent_assoc.to_vec(),
quote! { () },
None,
)]);
}
return ParseResult::Ok(vec![ImplSpec::new(
parent_types.to_vec(),
parent_trait.clone(),
parent_assoc.to_vec(),
tokens,
None,
)]);
}
}
}
if tv.len() >= 1 && matches!(&tv[0], TokenTree::Ident(id) if id == "fn") {
if tv.len() >= 2 {
if let TokenTree::Group(ref g) = tv[1] {
if g.delimiter() == proc_macro2::Delimiter::Parenthesis {
return ParseResult::Ok(vec![ImplSpec::new(
parent_types.to_vec(),
parent_trait.clone(),
parent_assoc.to_vec(),
tokens,
None,
)]);
}
}
}
return ParseResult::Ok(vec![ImplSpec::new(
parent_types.to_vec(),
parent_trait.clone(),
parent_assoc.to_vec(),
tokens,
None,
)]);
}
ParseResult::Ok(vec![ImplSpec::new(
parent_types.to_vec(),
parent_trait.clone(),
parent_assoc.to_vec(),
tokens,
None,
)])
}
fn dash_append_fn_keyword(
fn_token: &TokenTree,
slot: &SlotKind,
suffix: u64,
) -> (TokenStream2, Vec<TokenStream2>) {
let mut extra_types = Vec::new();
match slot {
SlotKind::Fixed(fixed_ts) => {
let fixed_tv: Vec<TokenTree> = fixed_ts.clone().into_iter().collect();
if fixed_tv.len() == 1 && matches!(&fixed_tv[0], TokenTree::Group(g) if g.delimiter() == proc_macro2::Delimiter::Parenthesis) {
let mut result = TokenStream2::new();
result.extend(std::iter::once(fn_token.clone()));
result.extend(fixed_ts.clone());
(result, extra_types)
} else {
let inner = proc_macro2::Group::new(proc_macro2::Delimiter::Parenthesis, fixed_ts.clone());
let mut result = TokenStream2::new();
result.extend(std::iter::once(fn_token.clone()));
result.extend(std::iter::once(TokenTree::Group(inner)));
(result, extra_types)
}
}
SlotKind::Bound(bound_ts) => {
let letter = crate::core::tuple::generic_letter(0, suffix);
extra_types.push(quote! { #letter: #bound_ts });
let inner = proc_macro2::Group::new(proc_macro2::Delimiter::Parenthesis, quote! { #letter });
let mut result = TokenStream2::new();
result.extend(std::iter::once(fn_token.clone()));
result.extend(std::iter::once(TokenTree::Group(inner)));
(result, extra_types)
}
}
}
fn dash_append_fn_type(
tuple_ts: &TokenStream2,
slot: &SlotKind,
suffix: u64,
) -> (TokenStream2, Vec<TokenStream2>) {
let mut extra_types = Vec::new();
match slot {
SlotKind::Fixed(fixed_ts) => {
let mut result = tuple_ts.clone();
result.extend(std::iter::once(TokenTree::Punct(proc_macro2::Punct::new('-', proc_macro2::Spacing::Joint))));
result.extend(std::iter::once(TokenTree::Punct(proc_macro2::Punct::new('>', proc_macro2::Spacing::Alone))));
result.extend(fixed_ts.clone());
(result, extra_types)
}
SlotKind::Bound(bound_ts) => {
let letter = crate::core::tuple::generic_letter(0, suffix);
extra_types.push(quote! { #letter: #bound_ts });
let mut result = TokenStream2::new();
result.extend(tuple_ts.clone());
result.extend(std::iter::once(TokenTree::Punct(proc_macro2::Punct::new('-', proc_macro2::Spacing::Joint))));
result.extend(std::iter::once(TokenTree::Punct(proc_macro2::Punct::new('>', proc_macro2::Spacing::Alone))));
result.extend(quote! { #letter });
(result, extra_types)
}
}
}
fn dash_append(tuple_ts: &TokenStream2, slot: &SlotKind, suffix: u64) -> (TokenStream2, Vec<TokenStream2>) {
let tv: Vec<TokenTree> = tuple_ts.clone().into_iter().collect();
if tv.len() == 1 && matches!(&tv[0], TokenTree::Ident(id) if id == "fn") {
return dash_append_fn_keyword(&tv[0], slot, suffix);
}
if tv.len() >= 2
&& matches!(&tv[0], TokenTree::Ident(id) if id == "fn")
&& matches!(&tv[1], TokenTree::Group(g) if g.delimiter() == proc_macro2::Delimiter::Parenthesis)
{
return dash_append_fn_type(tuple_ts, slot, suffix);
}
let mut extra_types = Vec::new();
let is_empty_tuple = tv.len() == 1
&& matches!(&tv[0], TokenTree::Group(g)
if g.delimiter() == proc_macro2::Delimiter::Parenthesis && g.stream().into_iter().next().is_none());
let is_tuple = tv.len() == 1
&& matches!(&tv[0], TokenTree::Group(g) if g.delimiter() == proc_macro2::Delimiter::Parenthesis);
match slot {
SlotKind::Fixed(fixed_ts) => {
if is_empty_tuple {
let ts = quote! { ( #fixed_ts ,) };
(ts, extra_types)
} else if is_tuple {
if let TokenTree::Group(g) = &tv[0] {
let content = g.stream();
let trimmed = strip_trailing_comma(content);
let mut new_inner = trimmed;
new_inner.extend(std::iter::once(TokenTree::Punct(proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone))));
new_inner.extend(fixed_ts.clone());
let new_group = proc_macro2::Group::new(g.delimiter(), new_inner);
let mut result = TokenStream2::new();
result.extend(std::iter::once(TokenTree::Group(new_group)));
(result, extra_types)
} else {
(tuple_ts.clone(), extra_types)
}
} else {
if let Some(new_ts) = append_to_generic_container(tuple_ts, fixed_ts) {
(new_ts, extra_types)
} else {
let ts = quote! { #tuple_ts < #fixed_ts > };
(ts, extra_types)
}
}
}
SlotKind::Bound(bound_ts) => {
let letter = crate::core::tuple::generic_letter(0, suffix);
extra_types.push(quote! { #letter: #bound_ts });
if is_empty_tuple {
let ts = quote! { ( #letter ,) };
(ts, extra_types)
} else if is_tuple {
if let TokenTree::Group(g) = &tv[0] {
let content = g.stream();
let trimmed = strip_trailing_comma(content);
let mut new_inner = trimmed;
new_inner.extend(std::iter::once(TokenTree::Punct(proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone))));
new_inner.extend(quote! { #letter });
let new_group = proc_macro2::Group::new(g.delimiter(), new_inner);
let mut result = TokenStream2::new();
result.extend(std::iter::once(TokenTree::Group(new_group)));
(result, extra_types)
} else {
(tuple_ts.clone(), extra_types)
}
} else {
let bound_letter: TokenStream2 = quote! { #letter };
if let Some(new_ts) = append_to_generic_container(tuple_ts, &bound_letter) {
(new_ts, extra_types)
} else {
let ts = quote! { #tuple_ts < #letter > };
(ts, extra_types)
}
}
}
}
}
fn append_to_generic_container(container_ts: &TokenStream2, new_arg: &TokenStream2) -> Option<TokenStream2> {
let tv: Vec<TokenTree> = container_ts.clone().into_iter().collect();
if tv.len() >= 3 {
if let (TokenTree::Ident(name), TokenTree::Punct(p)) = (&tv[0], &tv[1]) {
if p.as_char() == '<' {
let mut depth = 0u32;
let mut last_gt_pos = None;
for (i, tt) in tv.iter().enumerate().skip(2) {
if let TokenTree::Punct(pp) = tt {
if pp.as_char() == '<' {
depth += 1;
} else if pp.as_char() == '>' {
if depth == 0 {
last_gt_pos = Some(i);
break;
}
depth -= 1;
}
}
}
if let Some(gt_pos) = last_gt_pos {
if gt_pos == tv.len() - 1 {
let existing_args: Vec<&TokenTree> = tv[2..gt_pos].iter().collect();
let mut new_args = TokenStream2::new();
for (i, arg) in existing_args.iter().enumerate() {
if i > 0 {
new_args.extend(std::iter::once(TokenTree::Punct(
proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone),
)));
}
new_args.extend(std::iter::once((*arg).clone()));
}
new_args.extend(std::iter::once(TokenTree::Punct(
proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone),
)));
new_args.extend(new_arg.clone());
let mut result = TokenStream2::new();
result.extend(std::iter::once(TokenTree::Ident(name.clone())));
result.extend(std::iter::once(TokenTree::Punct(
proc_macro2::Punct::new('<', proc_macro2::Spacing::Alone),
)));
result.extend(new_args);
result.extend(std::iter::once(TokenTree::Punct(
proc_macro2::Punct::new('>', proc_macro2::Spacing::Alone),
)));
return Some(result);
}
}
}
}
}
None
}
pub fn dash_parse_slots_public(ts: &TokenStream2, span: Span) -> Result<Vec<SlotKind>, ParseResult> {
dash_parse_slots(ts, span)
}
pub fn dash_append_public(tuple_ts: &TokenStream2, slot: &SlotKind, suffix: u64) -> (TokenStream2, Vec<TokenStream2>) {
dash_append(tuple_ts, slot, suffix)
}