diff options
Diffstat (limited to 'vendor/gix-odb/src/store_impls/loose/verify.rs')
-rw-r--r-- | vendor/gix-odb/src/store_impls/loose/verify.rs | 103 |
1 files changed, 103 insertions, 0 deletions
diff --git a/vendor/gix-odb/src/store_impls/loose/verify.rs b/vendor/gix-odb/src/store_impls/loose/verify.rs new file mode 100644 index 000000000..648e5764c --- /dev/null +++ b/vendor/gix-odb/src/store_impls/loose/verify.rs @@ -0,0 +1,103 @@ +use std::{ + sync::atomic::{AtomicBool, Ordering}, + time::Instant, +}; + +use gix_features::progress::Progress; + +use crate::{loose::Store, Write}; + +/// +pub mod integrity { + /// The error returned by [`verify_integrity()`][super::Store::verify_integrity()]. + #[derive(Debug, thiserror::Error)] + #[allow(missing_docs)] + pub enum Error { + #[error("{kind} object {id} could not be decoded")] + ObjectDecode { + source: gix_object::decode::Error, + kind: gix_object::Kind, + id: gix_hash::ObjectId, + }, + #[error("{kind} object {expected} wasn't re-encoded without change - new hash is {actual}")] + ObjectHashMismatch { + kind: gix_object::Kind, + actual: gix_hash::ObjectId, + expected: gix_hash::ObjectId, + }, + #[error("Objects were deleted during iteration - try again")] + Retry, + #[error("Interrupted")] + Interrupted, + } + + /// The outcome returned by [`verify_integrity()`][super::Store::verify_integrity()]. + #[derive(Debug, PartialEq, Eq, Hash, Ord, PartialOrd, Clone)] + #[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))] + pub struct Statistics { + /// The amount of loose objects we checked. + pub num_objects: usize, + } + + /// The progress ids used in [`verify_integrity()`][super::Store::verify_integrity()]. + /// + /// Use this information to selectively extract the progress of interest in case the parent application has custom visualization. + #[derive(Debug, Copy, Clone)] + pub enum ProgressId { + /// The amount of loose objects that have been verified. + LooseObjects, + } + + impl From<ProgressId> for gix_features::progress::Id { + fn from(v: ProgressId) -> Self { + match v { + ProgressId::LooseObjects => *b"VILO", + } + } + } +} + +impl Store { + /// Check all loose objects for their integrity checking their hash matches the actual data and by decoding them fully. + pub fn verify_integrity( + &self, + mut progress: impl Progress, + should_interrupt: &AtomicBool, + ) -> Result<integrity::Statistics, integrity::Error> { + let mut buf = Vec::new(); + let sink = crate::sink(self.object_hash); + + let mut num_objects = 0; + let start = Instant::now(); + let mut progress = progress.add_child_with_id("Validating", integrity::ProgressId::LooseObjects.into()); + progress.init(None, gix_features::progress::count("loose objects")); + for id in self.iter().filter_map(Result::ok) { + let object = self + .try_find(id, &mut buf) + .map_err(|_| integrity::Error::Retry)? + .ok_or(integrity::Error::Retry)?; + let actual_id = sink.write_buf(object.kind, object.data).expect("sink never fails"); + if actual_id != id { + return Err(integrity::Error::ObjectHashMismatch { + kind: object.kind, + actual: actual_id, + expected: id, + }); + } + object.decode().map_err(|err| integrity::Error::ObjectDecode { + source: err, + kind: object.kind, + id, + })?; + + progress.inc(); + num_objects += 1; + if should_interrupt.load(Ordering::SeqCst) { + return Err(integrity::Error::Interrupted); + } + } + progress.show_throughput(start); + + Ok(integrity::Statistics { num_objects }) + } +} |