use proc_macro::{TokenStream, TokenTree};
use quote::{quote, quote_spanned};
use std::collections::HashSet;
use syn::{spanned::Spanned, Ident};
pub fn ensure_no_collision(value: Ident, haystack: TokenStream) -> Ident {
let idents = {
let mut unvisited = vec![haystack];
let mut found = HashSet::new();
while let Some(tokens) = unvisited.pop() {
for t in tokens {
match t {
TokenTree::Ident(ident) => {
found.insert(ident.to_string());
}
TokenTree::Group(g) => unvisited.push(g.stream()),
TokenTree::Punct(_) | TokenTree::Literal(_) => {}
}
}
}
found
};
let span = value.span();
let mut value = value.to_string();
while idents.contains(&value) {
value.push('X');
}
Ident::new(&value, span)
}
pub fn derive_label(
input: syn::DeriveInput,
trait_name: &str,
trait_path: &syn::Path,
) -> TokenStream {
if let syn::Data::Union(_) = &input.data {
let message = format!("Cannot derive {trait_name} for unions.");
return quote_spanned! {
input.span() => compile_error!(#message);
}
.into();
}
let ident = input.ident.clone();
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let mut where_clause = where_clause.cloned().unwrap_or_else(|| syn::WhereClause {
where_token: Default::default(),
predicates: Default::default(),
});
where_clause.predicates.push(
syn::parse2(quote! {
Self: 'static + Send + Sync + Clone + Eq + ::core::fmt::Debug + ::core::hash::Hash
})
.unwrap(),
);
quote! {
const _: () = {
extern crate alloc;
impl #impl_generics #trait_path for #ident #ty_generics #where_clause {
fn dyn_clone(&self) -> alloc::boxed::Box<dyn #trait_path> {
alloc::boxed::Box::new(::core::clone::Clone::clone(self))
}
}
};
}
.into()
}