use proc_macro2::{TokenStream as TokenStream2, TokenTree};
use std::collections::HashSet;
use syn::{GenericParam, Generics};
pub fn collect_ordered_type_params(generics: &Generics) -> Vec<String> {
generics
.params
.iter()
.filter_map(|param| match param {
GenericParam::Type(t) => Some(t.ident.to_string()),
_ => None,
})
.collect()
}
pub fn add_static_bounds(generics: &Generics) -> Generics {
let mut generics_with_static = generics.clone();
for param in generics_with_static.type_params_mut() {
param.bounds.push(syn::parse_quote!('static));
}
generics_with_static
}
pub fn strip_pattern_generics(pattern: &TokenStream2) -> TokenStream2 {
let mut result_tokens = Vec::new();
let mut skip_until_gt = false;
for tt in pattern.clone() {
match &tt {
TokenTree::Punct(p) if p.as_char() == '<' => {
skip_until_gt = true;
}
TokenTree::Punct(p) if p.as_char() == '>' && skip_until_gt => {
skip_until_gt = false;
continue;
}
_ if skip_until_gt => continue,
_ => result_tokens.push(tt),
}
}
result_tokens.into_iter().collect()
}
pub fn extract_trait_type_args(trait_type: &TokenStream2) -> Vec<Vec<TokenTree>> {
let mut trait_type_args = Vec::new();
let mut in_angles = false;
let mut current_arg = Vec::new();
for tt in trait_type.clone() {
match tt {
TokenTree::Punct(ref p) if p.as_char() == '<' => {
in_angles = true;
}
TokenTree::Punct(ref p) if p.as_char() == '>' => {
if !current_arg.is_empty() {
trait_type_args.push(current_arg.drain(..).collect());
}
break;
}
TokenTree::Punct(ref p) if p.as_char() == ',' && in_angles => {
if !current_arg.is_empty() {
trait_type_args.push(current_arg.drain(..).collect());
}
}
_ if in_angles => {
current_arg.push(tt);
}
_ => {}
}
}
trait_type_args
}
pub fn substitute_type_params(
sig_str: &str,
trait_type: &TokenStream2,
enum_params: &[String],
) -> String {
let trait_type_args = extract_trait_type_args(trait_type);
if trait_type_args.is_empty() {
return sig_str.to_string();
}
let mut result = sig_str.to_string();
for (i, enum_param) in enum_params.iter().enumerate() {
if i < trait_type_args.len() {
let placeholder = format!("__PLACEHOLDER_{}__", i);
result = result
.replace(&format!("& {}", enum_param), &format!("&{}", placeholder))
.replace(&format!("&{}", enum_param), &format!("&{}", placeholder))
.replace(&format!("( {}", enum_param), &format!("({}", placeholder))
.replace(&format!("({}", enum_param), &format!("({}", placeholder))
.replace(&format!("{} ,", enum_param), &format!("{},", placeholder))
.replace(&format!("{},", enum_param), &format!("{},", placeholder))
.replace(&format!("{} )", enum_param), &format!("{})", placeholder))
.replace(&format!("{})", enum_param), &format!("{})", placeholder))
.replace(
&format!("-> {}", enum_param),
&format!("-> {}", placeholder),
);
}
}
for (i, _) in enum_params.iter().enumerate() {
if i < trait_type_args.len() {
let trait_arg: TokenStream2 = trait_type_args[i].iter().cloned().collect();
let trait_arg_str = trait_arg.to_string().trim().to_string();
let placeholder = format!("__PLACEHOLDER_{}__", i);
result = result.replace(&placeholder, &trait_arg_str);
}
}
result
}
pub fn merge_generics(
variant_generics: &Generics,
enum_generics: &Generics,
used_enum_params: &HashSet<String>,
) -> Generics {
let mut merged = variant_generics.clone();
let variant_param_names: HashSet<String> = variant_generics
.type_params()
.map(|tp| tp.ident.to_string())
.collect();
for param in enum_generics.params.iter() {
match param {
GenericParam::Type(t) => {
let param_name = t.ident.to_string();
if used_enum_params.contains(¶m_name)
&& !variant_param_names.contains(¶m_name)
{
merged.params.push(param.clone());
}
}
_ => {} }
}
merged
}