forked from mirrors/jj
parent
a9f489ccdf
commit
8e1a6c708f
3 changed files with 38 additions and 7 deletions
|
@ -1,7 +1,18 @@
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use quote::{quote, quote_spanned};
|
use quote::{quote, quote_spanned};
|
||||||
use syn::spanned::Spanned;
|
use syn::spanned::Spanned;
|
||||||
use syn::{Data, Fields, Index};
|
use syn::{parse_quote, Data, Fields, GenericParam, Generics, Index};
|
||||||
|
|
||||||
|
pub fn add_trait_bounds(mut generics: Generics) -> Generics {
|
||||||
|
for param in &mut generics.params {
|
||||||
|
if let GenericParam::Type(ref mut type_param) = *param {
|
||||||
|
type_param
|
||||||
|
.bounds
|
||||||
|
.push(parse_quote!(::jj_lib::content_hash::ContentHash));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
generics
|
||||||
|
}
|
||||||
|
|
||||||
pub fn generate_hash_impl(data: &Data) -> TokenStream {
|
pub fn generate_hash_impl(data: &Data) -> TokenStream {
|
||||||
match *data {
|
match *data {
|
||||||
|
|
|
@ -18,10 +18,15 @@ pub fn derive_content_hash(input: proc_macro::TokenStream) -> proc_macro::TokenS
|
||||||
// Generate an expression to hash each of the fields in the struct.
|
// Generate an expression to hash each of the fields in the struct.
|
||||||
let hash_impl = content_hash::generate_hash_impl(&input.data);
|
let hash_impl = content_hash::generate_hash_impl(&input.data);
|
||||||
|
|
||||||
|
// Handle structs and enums with generics.
|
||||||
|
let generics = content_hash::add_trait_bounds(input.generics);
|
||||||
|
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
||||||
|
|
||||||
let expanded = quote! {
|
let expanded = quote! {
|
||||||
#[automatically_derived]
|
#[automatically_derived]
|
||||||
impl ::jj_lib::content_hash::ContentHash for #name {
|
impl #impl_generics ::jj_lib::content_hash::ContentHash for #name #ty_generics
|
||||||
fn hash(&self, state: &mut impl ::jj_lib::content_hash::DigestUpdate) {
|
#where_clause {
|
||||||
|
fn hash(&self, state: &mut impl digest::Update) {
|
||||||
#hash_impl
|
#hash_impl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -240,13 +240,28 @@ mod tests {
|
||||||
content_hash! {
|
content_hash! {
|
||||||
struct Foo { x: Vec<Option<i32>>, y: i64 }
|
struct Foo { x: Vec<Option<i32>>, y: i64 }
|
||||||
}
|
}
|
||||||
|
let foo_hash = hex::encode(hash(&Foo {
|
||||||
|
x: vec![None, Some(42)],
|
||||||
|
y: 17,
|
||||||
|
}));
|
||||||
insta::assert_snapshot!(
|
insta::assert_snapshot!(
|
||||||
hex::encode(hash(&Foo {
|
foo_hash,
|
||||||
x: vec![None, Some(42)],
|
|
||||||
y: 17
|
|
||||||
})),
|
|
||||||
@"e33c423b4b774b1353c414e0f9ef108822fde2fd5113fcd53bf7bd9e74e3206690b96af96373f268ed95dd020c7cbe171c7b7a6947fcaf5703ff6c8e208cefd4"
|
@"e33c423b4b774b1353c414e0f9ef108822fde2fd5113fcd53bf7bd9e74e3206690b96af96373f268ed95dd020c7cbe171c7b7a6947fcaf5703ff6c8e208cefd4"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Try again with an equivalent generic struct deriving ContentHash.
|
||||||
|
#[derive(ContentHash)]
|
||||||
|
struct GenericFoo<X, Y> {
|
||||||
|
x: X,
|
||||||
|
y: Y,
|
||||||
|
}
|
||||||
|
assert_eq!(
|
||||||
|
hex::encode(hash(&GenericFoo {
|
||||||
|
x: vec![None, Some(42)],
|
||||||
|
y: 17i64
|
||||||
|
})),
|
||||||
|
foo_hash
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This will be removed once all uses of content_hash! are replaced by the
|
// This will be removed once all uses of content_hash! are replaced by the
|
||||||
|
|
Loading…
Reference in a new issue