Skip to main content

gix_object/traits/
find.rs

1use gix_error::Result;
2/// Check if an object is present in an object store.
3pub trait Exists {
4    /// Returns `true` if the object exists in the database.
5    fn exists(&self, id: &gix_hash::oid) -> bool;
6}
7
8/// Find an object in the object store.
9///
10/// ## Notes
11///
12/// Find effectively needs [generic associated types][issue] to allow a trait for the returned object type.
13/// Until then, we will have to make due with explicit types and give them the potentially added features we want.
14///
15/// [issue]: https://github.com/rust-lang/rust/issues/44265
16pub trait Find {
17    /// Find an object matching `id` in the database while placing its raw, possibly encoded data into `buffer`.
18    ///
19    /// Returns `Some` object if it was present in the database, or the error that occurred during lookup or object
20    /// retrieval.
21    fn try_find<'a>(&self, id: &gix_hash::oid, buffer: &'a mut Vec<u8>) -> Result<Option<crate::Data<'a>>>;
22}
23
24/// Find the header of an object in the object store.
25pub trait Header {
26    /// Find the header of the object matching `id` in the database.
27    ///
28    /// Returns `Some` header if it was present, or the error that occurred during lookup.
29    fn try_header(&self, id: &gix_hash::oid) -> Result<Option<crate::Header>>;
30}
31
32/// A combination of [`Find`] and [`Header`] traits to help with `dyn` trait objects.
33pub trait FindObjectOrHeader: Find + Header {}
34
35mod _impls {
36    use gix_error::Result;
37    use std::{ops::Deref, rc::Rc, sync::Arc};
38
39    use gix_hash::oid;
40
41    use crate::Data;
42
43    impl<T> crate::Exists for &T
44    where
45        T: crate::Exists,
46    {
47        fn exists(&self, id: &oid) -> bool {
48            (*self).exists(id)
49        }
50    }
51
52    impl<T> crate::FindObjectOrHeader for T where T: crate::Find + crate::FindHeader {}
53
54    impl<T> crate::Find for &T
55    where
56        T: crate::Find,
57    {
58        fn try_find<'a>(&self, id: &oid, buffer: &'a mut Vec<u8>) -> Result<Option<Data<'a>>> {
59            (*self).try_find(id, buffer)
60        }
61    }
62
63    impl<T> crate::FindHeader for &T
64    where
65        T: crate::FindHeader,
66    {
67        fn try_header(&self, id: &gix_hash::oid) -> Result<Option<crate::Header>> {
68            (*self).try_header(id)
69        }
70    }
71
72    impl<T> crate::Exists for Box<T>
73    where
74        T: crate::Exists,
75    {
76        fn exists(&self, id: &oid) -> bool {
77            self.deref().exists(id)
78        }
79    }
80
81    impl<T> crate::Exists for Rc<T>
82    where
83        T: crate::Exists,
84    {
85        fn exists(&self, id: &oid) -> bool {
86            self.deref().exists(id)
87        }
88    }
89
90    impl<T> crate::Find for Rc<T>
91    where
92        T: crate::Find,
93    {
94        fn try_find<'a>(&self, id: &oid, buffer: &'a mut Vec<u8>) -> Result<Option<Data<'a>>> {
95            self.deref().try_find(id, buffer)
96        }
97    }
98
99    impl<T> crate::FindHeader for Rc<T>
100    where
101        T: crate::FindHeader,
102    {
103        fn try_header(&self, id: &gix_hash::oid) -> Result<Option<crate::Header>> {
104            self.deref().try_header(id)
105        }
106    }
107
108    impl<T> crate::Find for Box<T>
109    where
110        T: crate::Find,
111    {
112        fn try_find<'a>(&self, id: &oid, buffer: &'a mut Vec<u8>) -> Result<Option<Data<'a>>> {
113            self.deref().try_find(id, buffer)
114        }
115    }
116
117    impl<T> crate::FindHeader for Box<T>
118    where
119        T: crate::FindHeader,
120    {
121        fn try_header(&self, id: &gix_hash::oid) -> Result<Option<crate::Header>> {
122            self.deref().try_header(id)
123        }
124    }
125
126    impl<T> crate::Exists for Arc<T>
127    where
128        T: crate::Exists,
129    {
130        fn exists(&self, id: &oid) -> bool {
131            self.deref().exists(id)
132        }
133    }
134
135    impl<T> crate::Find for Arc<T>
136    where
137        T: crate::Find,
138    {
139        fn try_find<'a>(&self, id: &oid, buffer: &'a mut Vec<u8>) -> Result<Option<Data<'a>>> {
140            self.deref().try_find(id, buffer)
141        }
142    }
143
144    impl<T> crate::FindHeader for Arc<T>
145    where
146        T: crate::FindHeader,
147    {
148        fn try_header(&self, id: &gix_hash::oid) -> Result<Option<crate::Header>> {
149            self.deref().try_header(id)
150        }
151    }
152}
153
154mod ext {
155    use gix_error::{Error, Result, ResultExt, message};
156
157    use crate::{BlobRef, CommitRef, CommitRefIter, Kind, ObjectRef, TagRef, TagRefIter, TreeRef, TreeRefIter};
158
159    fn not_found(id: &gix_hash::oid) -> Error {
160        gix_error::message!("An object with id {id} could not be found").not_found_error()
161    }
162
163    fn wrong_kind(id: &gix_hash::oid, actual: Kind, expected: Kind) -> Error {
164        message!("Expected object of kind {expected} but got {actual} at {id}").validation_error()
165    }
166
167    macro_rules! make_obj_lookup {
168        ($method:ident, $object_variant:path, $object_kind:path, $object_type:ty) => {
169            /// Like [`find(…)`][Self::find()], but flattens the `Result<Option<_>>` into a single `Result` making a non-existing object an error
170            /// while returning the desired object type.
171            fn $method<'a>(&self, id: &gix_hash::oid, buffer: &'a mut Vec<u8>) -> Result<$object_type> {
172                self.try_find(id, buffer)?
173                    .ok_or_else(|| not_found(id))
174                    .and_then(|o| {
175                        o.decode()
176                            .or_raise(|| message!("Could not decode object at {id}").corrupted())
177                    })
178                    .and_then(|o| match o {
179                        $object_variant(o) => return Ok(o),
180                        o => Err(wrong_kind(id, o.kind(), $object_kind)),
181                    })
182            }
183        };
184    }
185
186    macro_rules! make_iter_lookup {
187        ($method:ident, $object_kind:path, $object_type:ty, $into_iter:tt) => {
188            /// Like [`find(…)`][Self::find()], but flattens the `Result<Option<_>>` into a single `Result` making a non-existing object an error
189            /// while returning the desired iterator type.
190            fn $method<'a>(&self, id: &gix_hash::oid, buffer: &'a mut Vec<u8>) -> Result<$object_type> {
191                self.try_find(id, buffer)?
192                    .ok_or_else(|| not_found(id))
193                    .and_then(|o| o.$into_iter().ok_or_else(|| wrong_kind(id, o.kind, $object_kind)))
194            }
195        };
196    }
197
198    /// An extension trait with convenience functions.
199    pub trait HeaderExt: super::Header {
200        /// Like [`try_header(…)`](super::Header::try_header()), but flattens the `Result<Option<_>>` into a single `Result` making a non-existing header an error.
201        fn header(&self, id: &gix_hash::oid) -> Result<crate::Header> {
202            self.try_header(id)?.ok_or_else(|| not_found(id))
203        }
204    }
205
206    /// An extension trait with convenience functions.
207    pub trait FindExt: super::Find {
208        /// Like [`try_find(…)`](super::Find::try_find()), but flattens the `Result<Option<_>>` into a single `Result` making a non-existing object an error.
209        fn find<'a>(&self, id: &gix_hash::oid, buffer: &'a mut Vec<u8>) -> Result<crate::Data<'a>> {
210            self.try_find(id, buffer)?.ok_or_else(|| not_found(id))
211        }
212
213        /// Like [`find(…)`][Self::find()], but flattens the `Result<Option<_>>` into a single `Result` making a non-existing object an error
214        /// while returning the desired object type.
215        fn find_blob<'a>(&self, id: &gix_hash::oid, buffer: &'a mut Vec<u8>) -> Result<BlobRef<'a>> {
216            if id == gix_hash::ObjectId::empty_blob(id.kind()) {
217                return Ok(BlobRef { data: &[] });
218            }
219            self.try_find(id, buffer)?
220                .ok_or_else(|| not_found(id))
221                .and_then(|o| {
222                    o.decode()
223                        .or_raise(|| message!("Could not decode object at {id}").corrupted())
224                })
225                .and_then(|o| match o {
226                    ObjectRef::Blob(o) => Ok(o),
227                    o => Err(wrong_kind(id, o.kind(), Kind::Blob)),
228                })
229        }
230
231        /// Like [`find(…)`][Self::find()], but flattens the `Result<Option<_>>` into a single `Result` making a non-existing object an error
232        /// while returning the desired object type.
233        fn find_tree<'a>(&self, id: &gix_hash::oid, buffer: &'a mut Vec<u8>) -> Result<TreeRef<'a>> {
234            if id == gix_hash::ObjectId::empty_tree(id.kind()) {
235                return Ok(TreeRef { entries: Vec::new() });
236            }
237            self.try_find(id, buffer)?
238                .ok_or_else(|| not_found(id))
239                .and_then(|o| {
240                    o.decode()
241                        .or_raise(|| message!("Could not decode object at {id}").corrupted())
242                })
243                .and_then(|o| match o {
244                    ObjectRef::Tree(o) => Ok(o),
245                    o => Err(wrong_kind(id, o.kind(), Kind::Tree)),
246                })
247        }
248
249        make_obj_lookup!(find_commit, ObjectRef::Commit, Kind::Commit, CommitRef<'a>);
250        make_obj_lookup!(find_tag, ObjectRef::Tag, Kind::Tag, TagRef<'a>);
251        make_iter_lookup!(find_commit_iter, Kind::Commit, CommitRefIter<'a>, try_into_commit_iter);
252        make_iter_lookup!(find_tree_iter, Kind::Tree, TreeRefIter<'a>, try_into_tree_iter);
253        make_iter_lookup!(find_tag_iter, Kind::Tag, TagRefIter<'a>, try_into_tag_iter);
254    }
255
256    impl<T: super::Find + ?Sized> FindExt for T {}
257}
258pub use ext::{FindExt, HeaderExt};