]>
Commit | Line | Data |
---|---|---|
0a29b90c FG |
1 | //! |
2 | use std::ops::Deref; | |
3 | ||
4 | use gix_hash::{oid, ObjectId}; | |
5 | ||
781aab86 | 6 | use crate::{object::find, Id, Object}; |
0a29b90c | 7 | |
781aab86 | 8 | /// An [object id][ObjectId] infused with a [`Repository`][crate::Repository]. |
0a29b90c FG |
9 | impl<'repo> Id<'repo> { |
10 | /// Find the [`Object`] associated with this object id, and consider it an error if it doesn't exist. | |
11 | /// | |
12 | /// # Note | |
13 | /// | |
14 | /// There can only be one `ObjectRef` per `Easy`. To increase that limit, clone the `Easy`. | |
15 | pub fn object(&self) -> Result<Object<'repo>, find::existing::Error> { | |
16 | self.repo.find_object(self.inner) | |
17 | } | |
18 | ||
781aab86 FG |
19 | /// Find the [`header`][gix_odb::find::Header] associated with this object id, or an error if it doesn't exist. |
20 | /// | |
21 | /// Use this method if there is no interest in the contents of the object, which generally is much faster to obtain. | |
22 | pub fn header(&self) -> Result<gix_odb::find::Header, find::existing::Error> { | |
23 | self.repo.find_header(self.inner) | |
24 | } | |
25 | ||
0a29b90c FG |
26 | /// Try to find the [`Object`] associated with this object id, and return `None` if it's not available locally. |
27 | /// | |
28 | /// # Note | |
29 | /// | |
30 | /// There can only be one `ObjectRef` per `Easy`. To increase that limit, clone the `Easy`. | |
31 | pub fn try_object(&self) -> Result<Option<Object<'repo>>, find::Error> { | |
32 | self.repo.try_find_object(self.inner) | |
33 | } | |
34 | ||
781aab86 FG |
35 | /// Find the [`header`][gix_odb::find::Header] associated with this object id, or return `None` if it doesn't exist. |
36 | /// | |
37 | /// Use this method if there is no interest in the contents of the object, which generally is much faster to obtain. | |
38 | pub fn try_header(&self) -> Result<Option<gix_odb::find::Header>, find::Error> { | |
39 | self.repo.try_find_header(self.inner) | |
40 | } | |
41 | ||
0a29b90c FG |
42 | /// Turn this object id into a shortened id with a length in hex as configured by `core.abbrev`. |
43 | pub fn shorten(&self) -> Result<gix_hash::Prefix, shorten::Error> { | |
44 | let hex_len = self.repo.config.hex_len.map_or_else( | |
45 | || self.repo.objects.packed_object_count().map(calculate_auto_hex_len), | |
46 | Ok, | |
47 | )?; | |
48 | ||
49 | let prefix = gix_odb::store::prefix::disambiguate::Candidate::new(self.inner, hex_len) | |
50 | .expect("BUG: internal hex-len must always be valid"); | |
51 | self.repo | |
52 | .objects | |
53 | .disambiguate_prefix(prefix)? | |
54 | .ok_or(shorten::Error::NotFound { oid: self.inner }) | |
55 | } | |
56 | ||
57 | /// Turn this object id into a shortened id with a length in hex as configured by `core.abbrev`, or default | |
58 | /// to a prefix which equals our id in the unlikely error case. | |
59 | pub fn shorten_or_id(&self) -> gix_hash::Prefix { | |
60 | self.shorten().unwrap_or_else(|_| self.inner.into()) | |
61 | } | |
62 | } | |
63 | ||
64 | fn calculate_auto_hex_len(num_packed_objects: u64) -> usize { | |
65 | let mut len = 64 - num_packed_objects.leading_zeros(); | |
66 | len = (len + 1) / 2; | |
67 | len.max(7) as usize | |
68 | } | |
69 | ||
70 | /// | |
71 | pub mod shorten { | |
72 | /// Returned by [`Id::prefix()`][super::Id::shorten()]. | |
73 | #[derive(Debug, thiserror::Error)] | |
74 | #[allow(missing_docs)] | |
75 | pub enum Error { | |
76 | #[error(transparent)] | |
77 | PackedObjectsCount(#[from] gix_odb::store::load_index::Error), | |
78 | #[error(transparent)] | |
79 | DisambiguatePrefix(#[from] gix_odb::store::prefix::disambiguate::Error), | |
80 | #[error("Id could not be shortened as the object with id {} could not be found", .oid)] | |
81 | NotFound { oid: gix_hash::ObjectId }, | |
82 | } | |
83 | } | |
84 | ||
85 | impl<'repo> Deref for Id<'repo> { | |
86 | type Target = oid; | |
87 | ||
88 | fn deref(&self) -> &Self::Target { | |
89 | &self.inner | |
90 | } | |
91 | } | |
92 | ||
93 | impl<'repo> Id<'repo> { | |
94 | pub(crate) fn from_id(id: impl Into<ObjectId>, repo: &'repo crate::Repository) -> Self { | |
95 | Id { inner: id.into(), repo } | |
96 | } | |
97 | ||
fe692bf9 | 98 | /// Turn this instance into its bare [`ObjectId`]. |
0a29b90c FG |
99 | pub fn detach(self) -> ObjectId { |
100 | self.inner | |
101 | } | |
102 | } | |
103 | ||
104 | impl<'repo> Id<'repo> { | |
105 | /// Obtain a platform for traversing ancestors of this commit. | |
781aab86 FG |
106 | pub fn ancestors(&self) -> crate::revision::walk::Platform<'repo> { |
107 | crate::revision::walk::Platform::new(Some(self.inner), self.repo) | |
0a29b90c FG |
108 | } |
109 | } | |
110 | ||
111 | mod impls { | |
112 | use std::{cmp::Ordering, hash::Hasher}; | |
113 | ||
114 | use gix_hash::{oid, ObjectId}; | |
115 | ||
116 | use crate::{Id, Object, ObjectDetached}; | |
117 | ||
118 | // Eq, Hash, Ord, PartialOrd, | |
119 | ||
120 | impl<'a> std::hash::Hash for Id<'a> { | |
121 | fn hash<H: Hasher>(&self, state: &mut H) { | |
122 | self.inner.hash(state) | |
123 | } | |
124 | } | |
125 | ||
126 | impl<'a> PartialOrd<Id<'a>> for Id<'a> { | |
127 | fn partial_cmp(&self, other: &Id<'a>) -> Option<Ordering> { | |
128 | self.inner.partial_cmp(&other.inner) | |
129 | } | |
130 | } | |
131 | ||
132 | impl<'repo> PartialEq<Id<'repo>> for Id<'repo> { | |
133 | fn eq(&self, other: &Id<'repo>) -> bool { | |
134 | self.inner == other.inner | |
135 | } | |
136 | } | |
137 | ||
138 | impl<'repo> PartialEq<ObjectId> for Id<'repo> { | |
139 | fn eq(&self, other: &ObjectId) -> bool { | |
140 | &self.inner == other | |
141 | } | |
142 | } | |
143 | ||
144 | impl<'repo> PartialEq<Id<'repo>> for ObjectId { | |
145 | fn eq(&self, other: &Id<'repo>) -> bool { | |
146 | self == &other.inner | |
147 | } | |
148 | } | |
149 | ||
150 | impl<'repo> PartialEq<oid> for Id<'repo> { | |
151 | fn eq(&self, other: &oid) -> bool { | |
152 | self.inner == other | |
153 | } | |
154 | } | |
155 | ||
156 | impl<'repo> PartialEq<Object<'repo>> for Id<'repo> { | |
157 | fn eq(&self, other: &Object<'repo>) -> bool { | |
158 | self.inner == other.id | |
159 | } | |
160 | } | |
161 | ||
162 | impl<'repo> PartialEq<ObjectDetached> for Id<'repo> { | |
163 | fn eq(&self, other: &ObjectDetached) -> bool { | |
164 | self.inner == other.id | |
165 | } | |
166 | } | |
167 | ||
168 | impl<'repo> std::fmt::Debug for Id<'repo> { | |
169 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | |
170 | self.inner.fmt(f) | |
171 | } | |
172 | } | |
173 | ||
174 | impl<'repo> std::fmt::Display for Id<'repo> { | |
175 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | |
176 | self.inner.fmt(f) | |
177 | } | |
178 | } | |
179 | ||
180 | impl<'repo> AsRef<oid> for Id<'repo> { | |
181 | fn as_ref(&self) -> &oid { | |
182 | &self.inner | |
183 | } | |
184 | } | |
185 | ||
186 | impl<'repo> From<Id<'repo>> for ObjectId { | |
187 | fn from(v: Id<'repo>) -> Self { | |
188 | v.inner | |
189 | } | |
190 | } | |
191 | } | |
192 | ||
193 | #[cfg(test)] | |
194 | mod tests { | |
195 | use super::*; | |
196 | ||
197 | #[test] | |
198 | fn size_of_oid() { | |
49aad941 FG |
199 | let actual = std::mem::size_of::<Id<'_>>(); |
200 | let ceiling = 32; | |
201 | assert!( | |
202 | actual <= ceiling, | |
203 | "size of oid shouldn't change without notice: {actual} <= {ceiling}" | |
0a29b90c FG |
204 | ) |
205 | } | |
206 | } |