use ahash::RandomState;
use syn::{parse_quote, visit_mut::VisitMut};
pub struct CleanDocComments;
impl CleanDocComments {
pub fn new() -> Self {
Self
}
}
impl VisitMut for CleanDocComments {
fn visit_attribute_mut(&mut self, attr: &mut syn::Attribute) {
if attr.path().is_ident("doc") {
*attr = parse_quote!(#[doc = ""]);
}
if attr.path().is_ident("freeze_struct") {
*attr = parse_quote!(#[freeze_struct]);
}
syn::visit_mut::visit_attribute_mut(self, attr);
}
}
pub fn generate_hash<T: Into<syn::Item> + Clone>(item: &T) -> u64 {
let item = item.clone();
const SEED1: u64 = 0x12345678;
const SEED2: u64 = 0x87654321;
let fixed_state = RandomState::with_seeds(SEED1, SEED2, SEED1, SEED2);
let item = Into::<syn::Item>::into(item);
fixed_state.hash_one(&item)
}
#[cfg(test)]
mod tests {
use super::*;
use syn::Item;
#[test]
fn test_clean_doc_comments() {
let item: Item = parse_quote! {
#[cfg(feature = "example")]
fn example() {
println!("Hello, world!");
}
};
let hash_before = generate_hash(&item);
let mut item_clone = item.clone();
let mut cleaner = CleanDocComments;
cleaner.visit_item_mut(&mut item_clone);
let hash_after = generate_hash(&item_clone);
assert_ne!(hash_before, hash_after);
let item2: Item = parse_quote! {
#[doc = ""]
#[cfg(feature = "example")]
fn example() {
println!("Hello, world!");
}
};
assert_eq!(hash_after, generate_hash(&item2));
}
#[test]
fn test_clean_doc_comments_struct() {
let item: Item = parse_quote! {
struct MyStruct {
#[cfg(feature = "field")]
field1: i32,
field2: String,
}
};
let hash_before = generate_hash(&item);
let mut item_clone = item.clone();
let mut cleaner = CleanDocComments;
cleaner.visit_item_mut(&mut item_clone);
let hash_after = generate_hash(&item_clone);
assert_ne!(hash_before, hash_after);
let item2: Item = parse_quote! {
#[doc = ""]
struct MyStruct {
#[cfg(feature = "field")]
field1: i32,
#[doc = ""]
field2: String,
}
};
assert_eq!(hash_after, generate_hash(&item2));
}
}