use std::hash::{Hash, Hasher};
use proc_macro2::{TokenStream as TokenStream2, TokenTree};
use quote::format_ident;
use syn::Ident;
struct Fnv(u64);
impl Default for Fnv {
fn default() -> Self {
Fnv(0xcbf2_9ce4_8422_2325)
}
}
impl Hasher for Fnv {
fn write(&mut self, bytes: &[u8]) {
for byte in bytes {
self.0 ^= u64::from(*byte);
self.0 = self.0.wrapping_mul(0x0000_0100_0000_01b3);
}
}
fn finish(&self) -> u64 {
self.0
}
}
pub(crate) fn trait_carrier(
ident: &Ident,
tokens: &TokenStream2,
name: Option<&Ident>,
line: usize,
column: usize,
) -> Ident {
match name {
Some(name) => format_ident!("__whichever_{}_{}", ident, name),
None => {
let mut hasher = Fnv::default();
tokens.to_string().hash(&mut hasher);
hash_token_positions(tokens.clone(), &mut hasher);
format_ident!(
"__whichever_{}_{:016x}_{}_{}",
ident,
hasher.finish(),
line,
column
)
}
}
}
pub(crate) fn subject_carrier(
ident: &Ident,
tokens: &TokenStream2,
line: usize,
column: usize,
) -> Ident {
let mut hasher = Fnv::default();
tokens.to_string().hash(&mut hasher);
format_ident!(
"__whichever_dispatch_{}_{:016x}_{}_{}",
ident,
hasher.finish(),
line,
column
)
}
fn hash_token_positions(tokens: TokenStream2, hasher: &mut Fnv) {
for tree in tokens {
let span = tree.span().unwrap();
(file_key(&span.file()), span.line(), span.column()).hash(hasher);
if let TokenTree::Group(group) = tree {
hash_token_positions(group.stream(), hasher);
}
}
}
fn file_key(path: &str) -> String {
let mut components = path.rsplit(['/', '\\']);
let file = components.next().unwrap_or_default();
match components.next() {
Some(directory) => format!("{directory}/{file}"),
None => file.to_owned(),
}
}