[go: up one dir, main page]

gix/
id.rs

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