From cb0a5cb704188964c58b1e15e7347b8b20e18370 Mon Sep 17 00:00:00 2001 From: Evan Mesterhazy Date: Wed, 14 Feb 2024 01:03:50 -0500 Subject: [PATCH] WIP: Make jj_lib proc macros useable by deps There are two things we need to do to make this work: 1. jj_lib needs to be able refer to itself as `jj_lib` which it can do by adding an `extern crate self as jj_lib` declaration. 2. jj_lib::content_hash needs to re-export the `digest::Update` type so that users of jj_lib can use the `#[derive(ContentHash)]` proc macro without directly depending on the digest crate. --- lib/proc-macros/src/content_hash.rs | 4 ++-- lib/proc-macros/src/lib.rs | 4 ++-- lib/src/backend.rs | 6 +++--- lib/src/content_hash.rs | 31 ++++++++++++++++------------- lib/src/lib.rs | 7 +++++++ lib/src/merge.rs | 4 ++-- lib/src/op_store.rs | 4 ++-- 7 files changed, 35 insertions(+), 25 deletions(-) diff --git a/lib/proc-macros/src/content_hash.rs b/lib/proc-macros/src/content_hash.rs index a487bb64ee5..060c4673a8c 100644 --- a/lib/proc-macros/src/content_hash.rs +++ b/lib/proc-macros/src/content_hash.rs @@ -10,7 +10,7 @@ pub fn generate_hash_impl(data: &Data) -> TokenStream { let hash_statements = fields.named.iter().map(|f| { let field_name = &f.ident; quote_spanned! {f.span()=> - crate::content_hash::ContentHash::hash(&self.#field_name, state); + jj_lib::content_hash::ContentHash::hash(&self.#field_name, state); // self.#field_name.hash(state); } }); @@ -22,7 +22,7 @@ pub fn generate_hash_impl(data: &Data) -> TokenStream { let hash_statements = fields.unnamed.iter().enumerate().map(|(i, f)| { let index = Index::from(i); quote_spanned! {f.span() => - crate::content_hash::ContentHash::hash(&self.#index, state); + jj_lib::content_hash::ContentHash::hash(&self.#index, state); } }); quote! { diff --git a/lib/proc-macros/src/lib.rs b/lib/proc-macros/src/lib.rs index cdbeae48292..07da35f2ab8 100644 --- a/lib/proc-macros/src/lib.rs +++ b/lib/proc-macros/src/lib.rs @@ -19,8 +19,8 @@ pub fn derive_content_hash(input: proc_macro::TokenStream) -> proc_macro::TokenS let hash_impl = content_hash::generate_hash_impl(&input.data); let expanded = quote! { - impl crate::content_hash::ContentHash for #name { - fn hash(&self, state: &mut impl digest::Update) { + impl jj_lib::content_hash::ContentHash for #name { + fn hash(&self, state: &mut impl jj_lib::content_hash::DigestUpdate) { #hash_impl } } diff --git a/lib/src/backend.rs b/lib/src/backend.rs index 76779804b34..5a55c90e4f3 100644 --- a/lib/src/backend.rs +++ b/lib/src/backend.rs @@ -25,7 +25,7 @@ use std::vec::Vec; use async_trait::async_trait; use thiserror::Error; -use crate::content_hash::ContentHash; +use crate::content_hash::{ContentHash, DigestUpdate}; use crate::index::Index; use crate::merge::Merge; use crate::object_id::{id_type, ObjectId}; @@ -111,7 +111,7 @@ impl PartialEq for MergedTreeId { impl Eq for MergedTreeId {} impl ContentHash for MergedTreeId { - fn hash(&self, state: &mut impl digest::Update) { + fn hash(&self, state: &mut impl DigestUpdate) { match self { MergedTreeId::Legacy(tree_id) => { state.update(b"0"); @@ -247,7 +247,7 @@ impl TreeValue { } impl ContentHash for TreeValue { - fn hash(&self, state: &mut impl digest::Update) { + fn hash(&self, state: &mut impl DigestUpdate) { use TreeValue::*; match self { File { id, executable } => { diff --git a/lib/src/content_hash.rs b/lib/src/content_hash.rs index a36acb1f7b9..06857dacb4c 100644 --- a/lib/src/content_hash.rs +++ b/lib/src/content_hash.rs @@ -1,8 +1,11 @@ //! Portable, stable hashing suitable for identifying values use blake2::Blake2b512; +// Re-export DigestUpdate so that the ContentHash proc macro can be used in +// external crates without directly depending on the digest crate. +pub use digest::Update as DigestUpdate; use itertools::Itertools as _; -pub(crate) use jj_lib_proc_macros::ContentHash; +pub use jj_lib_proc_macros::ContentHash; /// Portable, stable hashing suitable for identifying values /// @@ -13,7 +16,7 @@ pub(crate) use jj_lib_proc_macros::ContentHash; /// variant, then the variant's fields in lexical order. pub trait ContentHash { /// Update the hasher state with this object's content - fn hash(&self, state: &mut impl digest::Update); + fn hash(&self, state: &mut impl DigestUpdate); } /// The 512-bit BLAKE2b content hash @@ -25,36 +28,36 @@ pub fn blake2b_hash(x: &(impl ContentHash + ?Sized)) -> digest::Output ContentHash for [T] { - fn hash(&self, state: &mut impl digest::Update) { + fn hash(&self, state: &mut impl DigestUpdate) { state.update(&(self.len() as u64).to_le_bytes()); for x in self { x.hash(state); @@ -63,19 +66,19 @@ impl ContentHash for [T] { } impl ContentHash for Vec { - fn hash(&self, state: &mut impl digest::Update) { + fn hash(&self, state: &mut impl DigestUpdate) { self.as_slice().hash(state) } } impl ContentHash for String { - fn hash(&self, state: &mut impl digest::Update) { + fn hash(&self, state: &mut impl DigestUpdate) { self.as_bytes().hash(state); } } impl ContentHash for Option { - fn hash(&self, state: &mut impl digest::Update) { + fn hash(&self, state: &mut impl DigestUpdate) { match self { None => state.update(&[0]), Some(x) => { @@ -91,7 +94,7 @@ where K: ContentHash + Ord, V: ContentHash, { - fn hash(&self, state: &mut impl digest::Update) { + fn hash(&self, state: &mut impl DigestUpdate) { state.update(&(self.len() as u64).to_le_bytes()); let mut kv = self.iter().collect_vec(); kv.sort_unstable_by_key(|&(k, _)| k); @@ -106,7 +109,7 @@ impl ContentHash for std::collections::HashSet where K: ContentHash + Ord, { - fn hash(&self, state: &mut impl digest::Update) { + fn hash(&self, state: &mut impl DigestUpdate) { state.update(&(self.len() as u64).to_le_bytes()); for k in self.iter().sorted() { k.hash(state); @@ -119,7 +122,7 @@ where K: ContentHash, V: ContentHash, { - fn hash(&self, state: &mut impl digest::Update) { + fn hash(&self, state: &mut impl DigestUpdate) { state.update(&(self.len() as u64).to_le_bytes()); for (k, v) in self.iter() { k.hash(state); diff --git a/lib/src/lib.rs b/lib/src/lib.rs index 2340a3bb1a1..a469da3e97d 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -18,6 +18,13 @@ #![deny(unused_must_use)] #![forbid(unsafe_code)] +// Needed so that proc macros can be used inside jj_lib and by external crates +// that depend on it. +// See: +// - https://github.com/rust-lang/rust/issues/54647#issuecomment-432015102 +// - https://github.com/rust-lang/rust/issues/54363 +extern crate self as jj_lib; + #[macro_use] pub mod content_hash; diff --git a/lib/src/merge.rs b/lib/src/merge.rs index 435102bce78..b5d66a9152c 100644 --- a/lib/src/merge.rs +++ b/lib/src/merge.rs @@ -29,7 +29,7 @@ use smallvec::{smallvec_inline, SmallVec}; use crate::backend; use crate::backend::{BackendError, FileId, TreeId, TreeValue}; -use crate::content_hash::ContentHash; +use crate::content_hash::{ContentHash, DigestUpdate}; use crate::object_id::ObjectId; use crate::repo_path::RepoPath; use crate::store::Store; @@ -457,7 +457,7 @@ impl Merge> { } impl ContentHash for Merge { - fn hash(&self, state: &mut impl digest::Update) { + fn hash(&self, state: &mut impl DigestUpdate) { self.values.hash(state) } } diff --git a/lib/src/op_store.rs b/lib/src/op_store.rs index 9b6380d932c..25c2aad92ee 100644 --- a/lib/src/op_store.rs +++ b/lib/src/op_store.rs @@ -25,7 +25,7 @@ use once_cell::sync::Lazy; use thiserror::Error; use crate::backend::{CommitId, MillisSinceEpoch, Timestamp}; -use crate::content_hash::ContentHash; +use crate::content_hash::{ContentHash, DigestUpdate}; use crate::merge::Merge; use crate::object_id::{id_type, HexPrefix, ObjectId, PrefixResolution}; @@ -213,7 +213,7 @@ pub enum RemoteRefState { } impl ContentHash for RemoteRefState { - fn hash(&self, state: &mut impl digest::Update) { + fn hash(&self, state: &mut impl DigestUpdate) { (*self as u8).hash(state); } }