use proc_macro2::{TokenStream, TokenTree};
use quote::quote;
use syn::ItemTrait;
use crate::analyze::TraitBounds;
use crate::util::scan_stop;
fn args_all_bindings(args: &[TokenTree]) -> bool {
let mut rest = args;
while let Some(idx) = scan_stop(rest, &[',']) {
if scan_stop(&rest[..idx], &['=']).is_none() {
return false;
}
rest = &rest[idx + 1..];
}
scan_stop(rest, &['=']).is_some()
}
fn render_formals(
trait_def: &ItemTrait, trait_bounds: &TraitBounds,
) -> Vec<TokenStream> {
let mut formals = vec![];
for (i, p) in trait_def.generics.params.iter().enumerate() {
match p {
syn::GenericParam::Lifetime(_) | syn::GenericParam::Const(_) => {
formals.push(quote!(#p));
}
syn::GenericParam::Type(tp) => {
let id = &tp.ident;
match trait_bounds.params.get(i).and_then(|t| t.bound.clone()) {
Some(b) => formals.push(quote!(#id: #b)),
None => formals.push(quote!(#id)),
}
}
}
}
formals
}
pub(crate) fn expand_empty_trait_generics(
tokens: &[TokenTree], trait_def: &ItemTrait, trait_bounds: &TraitBounds,
) -> Result<Vec<TokenTree>, TokenStream> {
if trait_def.generics.params.is_empty() {
return Ok(tokens.to_vec());
}
let arg_names = crate::analyze::generic_param_names(&trait_def.generics);
let formals = render_formals(trait_def, trait_bounds);
let mut out = vec![];
let mut i = 0;
while i < tokens.len() {
match &tokens[i] {
TokenTree::Ident(id) => {
let group = match tokens.get(i + 1) {
Some(TokenTree::Group(g)) if g.delimiter() == delimiter![<>] => g,
_ => {
out.push(tokens[i].clone());
i += 1;
continue;
}
};
let args = group.stream().into_iter().collect::<Vec<TokenTree>>();
let bindings_only = !args.is_empty() && args_all_bindings(&args);
if args.is_empty() || bindings_only {
out.push(
proc_macro2::Group::new(
delimiter![<>],
quote!(#(#formals),*),
)
.into(),
);
out.extend(quote!(#id));
let args_ts = if args.is_empty() {
quote!(#(#arg_names),*)
} else {
let bind_ts = args.iter().cloned().collect::<TokenStream>();
quote!(#(#arg_names),* , #bind_ts)
};
out.push(proc_macro2::Group::new(delimiter![<>], args_ts).into());
i += 2;
} else {
out.push(tokens[i].clone());
i += 1;
}
}
_ => {
out.push(tokens[i].clone());
i += 1;
}
}
}
Ok(out)
}