gix_object/traits/
find.rs

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