gix_odb/store_impls/loose/
verify.rs1use std::{
2 sync::atomic::{AtomicBool, Ordering},
3 time::Instant,
4};
5
6use gix_features::progress::{Count, DynNestedProgress, Progress};
7
8use crate::loose::Store;
9
10pub mod integrity {
12 #[derive(Debug, thiserror::Error)]
14 #[allow(missing_docs)]
15 pub enum Error {
16 #[error("{kind} object {id} could not be decoded")]
17 ObjectDecode {
18 source: gix_object::decode::Error,
19 kind: gix_object::Kind,
20 id: gix_hash::ObjectId,
21 },
22 #[error("{kind} object {expected} could not be hashed")]
23 ObjectHasher {
24 #[source]
25 source: gix_hash::hasher::Error,
26 kind: gix_object::Kind,
27 expected: gix_hash::ObjectId,
28 },
29 #[error("{kind} object wasn't re-encoded without change")]
30 ObjectEncodeMismatch {
31 #[source]
32 source: gix_hash::verify::Error,
33 kind: gix_object::Kind,
34 },
35 #[error("Objects were deleted during iteration - try again")]
36 Retry,
37 #[error("Interrupted")]
38 Interrupted,
39 }
40
41 #[derive(Debug, PartialEq, Eq, Hash, Ord, PartialOrd, Clone)]
43 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
44 pub struct Statistics {
45 pub num_objects: usize,
47 }
48
49 #[derive(Debug, Copy, Clone)]
53 pub enum ProgressId {
54 LooseObjects,
56 }
57
58 impl From<ProgressId> for gix_features::progress::Id {
59 fn from(v: ProgressId) -> Self {
60 match v {
61 ProgressId::LooseObjects => *b"VILO",
62 }
63 }
64 }
65}
66
67impl Store {
68 pub fn verify_integrity(
70 &self,
71 progress: &mut dyn DynNestedProgress,
72 should_interrupt: &AtomicBool,
73 ) -> Result<integrity::Statistics, integrity::Error> {
74 use gix_object::Write;
75 let mut buf = Vec::new();
76 let sink = crate::sink(self.object_hash);
77
78 let mut num_objects = 0;
79 let start = Instant::now();
80 let mut progress = progress.add_child_with_id("Validating".into(), integrity::ProgressId::LooseObjects.into());
81 progress.init(None, gix_features::progress::count("loose objects"));
82 for id in self.iter().filter_map(Result::ok) {
83 let object = self
84 .try_find(&id, &mut buf)
85 .map_err(|_| integrity::Error::Retry)?
86 .ok_or(integrity::Error::Retry)?;
87 sink.write_buf(object.kind, object.data)
88 .map_err(|err| integrity::Error::ObjectHasher {
89 source: *err.downcast().expect("sink can only fail in hasher"),
90 kind: object.kind,
91 expected: id,
92 })?
93 .verify(&id)
94 .map_err(|err| integrity::Error::ObjectEncodeMismatch {
95 source: err,
96 kind: object.kind,
97 })?;
98 object.decode().map_err(|err| integrity::Error::ObjectDecode {
99 source: err,
100 kind: object.kind,
101 id,
102 })?;
103
104 progress.inc();
105 num_objects += 1;
106 if should_interrupt.load(Ordering::SeqCst) {
107 return Err(integrity::Error::Interrupted);
108 }
109 }
110 progress.show_throughput(start);
111
112 Ok(integrity::Statistics { num_objects })
113 }
114}