1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
use crate::pack;
use git_object::immutable;
#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone)]
pub struct Object<'a> {
pub kind: git_object::Kind,
pub data: &'a [u8],
pub(crate) pack_location: Option<pack::bundle::Location>,
}
impl<'a> Object<'a> {
pub fn new(kind: git_object::Kind, data: &'a [u8]) -> Object<'a> {
Object {
kind,
data,
pack_location: None,
}
}
pub fn decode(&self) -> Result<immutable::Object<'a>, immutable::object::decode::Error> {
Ok(match self.kind {
git_object::Kind::Tree => immutable::Object::Tree(immutable::Tree::from_bytes(self.data)?),
git_object::Kind::Blob => immutable::Object::Blob(immutable::Blob { data: self.data }),
git_object::Kind::Commit => immutable::Object::Commit(immutable::Commit::from_bytes(self.data)?),
git_object::Kind::Tag => immutable::Object::Tag(immutable::Tag::from_bytes(self.data)?),
})
}
pub fn into_tree_iter(self) -> Option<immutable::TreeIter<'a>> {
match self.kind {
git_object::Kind::Tree => Some(immutable::TreeIter::from_bytes(self.data)),
_ => None,
}
}
pub fn into_commit_iter(self) -> Option<immutable::CommitIter<'a>> {
match self.kind {
git_object::Kind::Commit => Some(immutable::CommitIter::from_bytes(self.data)),
_ => None,
}
}
}
pub mod verify {
use crate::{hash, loose};
use std::io;
#[derive(thiserror::Error, Debug)]
#[allow(missing_docs)]
pub enum Error {
#[error("Object expected to have id {desired}, but actual id was {actual}")]
ChecksumMismatch {
desired: git_hash::ObjectId,
actual: git_hash::ObjectId,
},
}
impl crate::data::Object<'_> {
pub fn verify_checksum(&self, desired: impl AsRef<git_hash::oid>) -> Result<(), Error> {
let desired = desired.as_ref();
let mut sink = hash::Write::new(io::sink(), desired.kind());
loose::object::header::encode(self.kind, self.data.len() as u64, &mut sink).expect("hash to always work");
sink.hash.update(&self.data);
let actual_id = git_hash::ObjectId::from(sink.hash.digest());
if desired != actual_id {
return Err(Error::ChecksumMismatch {
desired: desired.into(),
actual: actual_id,
});
}
Ok(())
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn size_of_object() {
assert_eq!(std::mem::size_of::<Object<'_>>(), 48, "this shouldn't change unnoticed");
}
}