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>>,
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,
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 {
type_params: all_types,
trait_params: spec.trait_params.clone(),
target: new_target,
custom_body: spec.custom_body.clone(),
is_unsafe: false,
});
}
}
ParseResult::Ok(specs)
} else {
dash_parse_start(tokens, parent_types, parent_trait, 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>>,
_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 {
type_params: parent_types.to_vec(),
trait_params: parent_trait.clone(),
target: quote! { () },
custom_body: None,
is_unsafe: false,
}]);
}
return ParseResult::Ok(vec![ImplSpec {
type_params: parent_types.to_vec(),
trait_params: parent_trait.clone(),
target: tokens,
custom_body: None,
is_unsafe: false,
}]);
}
}
}
ParseResult::Ok(vec![ImplSpec {
type_params: parent_types.to_vec(),
trait_params: parent_trait.clone(),
target: tokens,
custom_body: None,
is_unsafe: false,
}])
}
fn dash_append(tuple_ts: &TokenStream2, slot: &SlotKind, suffix: u64) -> (TokenStream2, Vec<TokenStream2>) {
let tv: Vec<TokenTree> = tuple_ts.clone().into_iter().collect();
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 {
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 ts = quote! { #tuple_ts < #letter > };
(ts, extra_types)
}
}
}
}