1use std::{
2 borrow::Borrow,
3 convert::TryInto,
4 fmt,
5 hash::{Hash, Hasher},
6 ops::Deref,
7};
8
9use crate::{borrowed::oid, Kind, SIZE_OF_SHA1_DIGEST};
10
11#[derive(PartialEq, Eq, Ord, PartialOrd, Clone, Copy)]
13#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
14pub enum ObjectId {
15 Sha1([u8; SIZE_OF_SHA1_DIGEST]),
17}
18
19#[allow(clippy::derive_hash_xor_eq)]
26impl Hash for ObjectId {
27 fn hash<H: Hasher>(&self, state: &mut H) {
28 state.write(self.as_slice())
29 }
30}
31
32#[allow(missing_docs)]
33pub mod decode {
34 use std::str::FromStr;
35
36 use crate::object_id::ObjectId;
37
38 #[derive(Debug, thiserror::Error)]
40 #[allow(missing_docs)]
41 pub enum Error {
42 #[error("A hash sized {0} hexadecimal characters is invalid")]
43 InvalidHexEncodingLength(usize),
44 #[error("Invalid character {c} at position {index}")]
45 Invalid { c: char, index: usize },
46 }
47
48 impl ObjectId {
50 pub fn from_hex(buffer: &[u8]) -> Result<ObjectId, Error> {
54 use hex::FromHex;
55 match buffer.len() {
56 40 => Ok(ObjectId::Sha1(<[u8; 20]>::from_hex(buffer).map_err(
57 |err| match err {
58 hex::FromHexError::InvalidHexCharacter { c, index } => Error::Invalid { c, index },
59 hex::FromHexError::OddLength | hex::FromHexError::InvalidStringLength => {
60 unreachable!("BUG: This is already checked")
61 }
62 },
63 )?)),
64 len => Err(Error::InvalidHexEncodingLength(len)),
65 }
66 }
67 }
68
69 impl FromStr for ObjectId {
70 type Err = Error;
71
72 fn from_str(s: &str) -> Result<Self, Self::Err> {
73 Self::from_hex(s.as_bytes())
74 }
75 }
76}
77
78impl ObjectId {
80 #[inline]
82 pub fn kind(&self) -> crate::Kind {
83 match self {
84 ObjectId::Sha1(_) => crate::Kind::Sha1,
85 }
86 }
87 #[inline]
89 pub fn as_slice(&self) -> &[u8] {
90 match self {
91 Self::Sha1(b) => b.as_ref(),
92 }
93 }
94 #[inline]
96 pub fn as_mut_slice(&mut self) -> &mut [u8] {
97 match self {
98 Self::Sha1(b) => b.as_mut(),
99 }
100 }
101
102 #[inline]
104 pub const fn empty_tree(hash: Kind) -> ObjectId {
105 match hash {
106 Kind::Sha1 => {
107 ObjectId::Sha1(*b"\x4b\x82\x5d\xc6\x42\xcb\x6e\xb9\xa0\x60\xe5\x4b\xf8\xd6\x92\x88\xfb\xee\x49\x04")
108 }
109 }
110 }
111
112 #[inline]
114 pub fn is_null(&self) -> bool {
115 match self {
116 ObjectId::Sha1(digest) => &digest[..] == oid::null_sha1().as_bytes(),
117 }
118 }
119
120 #[inline]
122 pub const fn null(kind: crate::Kind) -> ObjectId {
123 match kind {
124 crate::Kind::Sha1 => Self::null_sha1(),
125 }
126 }
127}
128
129impl ObjectId {
131 #[inline]
133 fn new_sha1(id: [u8; SIZE_OF_SHA1_DIGEST]) -> Self {
134 ObjectId::Sha1(id)
135 }
136
137 #[inline]
141 pub(crate) fn from_20_bytes(b: &[u8]) -> ObjectId {
142 let mut id = [0; SIZE_OF_SHA1_DIGEST];
143 id.copy_from_slice(b);
144 ObjectId::Sha1(id)
145 }
146
147 #[inline]
149 pub(crate) const fn null_sha1() -> ObjectId {
150 ObjectId::Sha1([0u8; 20])
151 }
152}
153
154impl std::fmt::Debug for ObjectId {
155 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
156 match self {
157 ObjectId::Sha1(_hash) => f.write_str("Sha1(")?,
158 }
159 for b in self.as_bytes() {
160 write!(f, "{b:02x}")?;
161 }
162 f.write_str(")")
163 }
164}
165
166impl From<[u8; SIZE_OF_SHA1_DIGEST]> for ObjectId {
167 fn from(v: [u8; 20]) -> Self {
168 Self::new_sha1(v)
169 }
170}
171
172impl From<&[u8]> for ObjectId {
173 fn from(v: &[u8]) -> Self {
174 match v.len() {
175 20 => Self::Sha1(v.try_into().expect("prior length validation")),
176 other => panic!("BUG: unsupported hash len: {other}"),
177 }
178 }
179}
180
181impl From<&crate::oid> for ObjectId {
182 fn from(v: &oid) -> Self {
183 match v.kind() {
184 crate::Kind::Sha1 => ObjectId::from_20_bytes(v.as_bytes()),
185 }
186 }
187}
188
189impl Deref for ObjectId {
190 type Target = oid;
191
192 fn deref(&self) -> &Self::Target {
193 self.as_ref()
194 }
195}
196
197impl AsRef<crate::oid> for ObjectId {
198 fn as_ref(&self) -> &oid {
199 oid::from_bytes_unchecked(self.as_slice())
200 }
201}
202
203impl Borrow<crate::oid> for ObjectId {
204 fn borrow(&self) -> &oid {
205 self.as_ref()
206 }
207}
208
209impl fmt::Display for ObjectId {
210 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
211 write!(f, "{}", self.to_hex())
212 }
213}
214
215impl PartialEq<&crate::oid> for ObjectId {
216 fn eq(&self, other: &&oid) -> bool {
217 self.as_ref() == *other
218 }
219}