gix_pack/
find_traits.rs

1use crate::{data, find};
2
3/// Describe how object can be located in an object store with built-in facilities to supports packs specifically.
4///
5/// ## Notes
6///
7/// Find effectively needs [generic associated types][issue] to allow a trait for the returned object type.
8/// Until then, we will have to make due with explicit types and give them the potentially added features we want.
9///
10/// Furthermore, despite this trait being in `gix-pack`, it leaks knowledge about objects potentially not being packed.
11/// This is a necessary trade-off to allow this trait to live in `gix-pack` where it is used in functions to create a pack.
12///
13/// [issue]: https://github.com/rust-lang/rust/issues/44265
14pub trait Find {
15    /// Returns true if the object exists in the database.
16    fn contains(&self, id: &gix_hash::oid) -> bool;
17
18    /// Find an object matching `id` in the database while placing its raw, decoded data into `buffer`.
19    /// A `pack_cache` can be used to speed up subsequent lookups, set it to [`crate::cache::Never`] if the
20    /// workload isn't suitable for caching.
21    ///
22    /// Returns `Some((<object data>, <pack location if packed>))` if it was present in the database,
23    /// or the error that occurred during lookup or object retrieval.
24    fn try_find<'a>(
25        &self,
26        id: &gix_hash::oid,
27        buffer: &'a mut Vec<u8>,
28    ) -> Result<Option<(gix_object::Data<'a>, Option<data::entry::Location>)>, gix_object::find::Error> {
29        self.try_find_cached(id, buffer, &mut crate::cache::Never)
30    }
31
32    /// Like [`Find::try_find()`], but with support for controlling the pack cache.
33    /// A `pack_cache` can be used to speed up subsequent lookups, set it to [`crate::cache::Never`] if the
34    /// workload isn't suitable for caching.
35    ///
36    /// Returns `Some((<object data>, <pack location if packed>))` if it was present in the database,
37    /// or the error that occurred during lookup or object retrieval.
38    fn try_find_cached<'a>(
39        &self,
40        id: &gix_hash::oid,
41        buffer: &'a mut Vec<u8>,
42        pack_cache: &mut dyn crate::cache::DecodeEntry,
43    ) -> Result<Option<(gix_object::Data<'a>, Option<data::entry::Location>)>, gix_object::find::Error>;
44
45    /// Find the packs location where an object with `id` can be found in the database, or `None` if there is no pack
46    /// holding the object.
47    ///
48    /// _Note_ that this is always None if the object isn't packed even though it exists as loose object.
49    fn location_by_oid(&self, id: &gix_hash::oid, buf: &mut Vec<u8>) -> Option<data::entry::Location>;
50
51    /// Obtain a vector of all offsets, in index order, along with their object id.
52    fn pack_offsets_and_oid(&self, pack_id: u32) -> Option<Vec<(data::Offset, gix_hash::ObjectId)>>;
53
54    /// Return the [`find::Entry`] for `location` if it is backed by a pack.
55    ///
56    /// Note that this is only in the interest of avoiding duplicate work during pack generation.
57    /// Pack locations can be obtained from [`Find::try_find()`].
58    ///
59    /// # Notes
60    ///
61    /// Custom implementations might be interested in providing their own meta-data with `object`,
62    /// which currently isn't possible as the `Locate` trait requires GATs to work like that.
63    fn entry_by_location(&self, location: &data::entry::Location) -> Option<find::Entry>;
64}
65
66mod ext {
67    use gix_object::{BlobRef, CommitRef, CommitRefIter, Kind, ObjectRef, TagRef, TagRefIter, TreeRef, TreeRefIter};
68
69    macro_rules! make_obj_lookup {
70        ($method:ident, $object_variant:path, $object_kind:path, $object_type:ty) => {
71            /// Like [`find(…)`][Self::find()], but flattens the `Result<Option<_>>` into a single `Result` making a non-existing object an error
72            /// while returning the desired object type.
73            fn $method<'a>(
74                &self,
75                id: &gix_hash::oid,
76                buffer: &'a mut Vec<u8>,
77            ) -> Result<($object_type, Option<crate::data::entry::Location>), gix_object::find::existing_object::Error>
78            {
79                let id = id.as_ref();
80                self.try_find(id, buffer)
81                    .map_err(gix_object::find::existing_object::Error::Find)?
82                    .ok_or_else(|| gix_object::find::existing_object::Error::NotFound {
83                        oid: id.as_ref().to_owned(),
84                    })
85                    .and_then(|(o, l)| {
86                        o.decode()
87                            .map_err(|err| gix_object::find::existing_object::Error::Decode {
88                                source: err,
89                                oid: id.to_owned(),
90                            })
91                            .map(|o| (o, l))
92                    })
93                    .and_then(|(o, l)| match o {
94                        $object_variant(o) => return Ok((o, l)),
95                        o => Err(gix_object::find::existing_object::Error::ObjectKind {
96                            oid: id.to_owned(),
97                            actual: o.kind(),
98                            expected: $object_kind,
99                        }),
100                    })
101            }
102        };
103    }
104
105    macro_rules! make_iter_lookup {
106        ($method:ident, $object_kind:path, $object_type:ty, $into_iter:tt) => {
107            /// Like [`find(…)`][Self::find()], but flattens the `Result<Option<_>>` into a single `Result` making a non-existing object an error
108            /// while returning the desired iterator type.
109            fn $method<'a>(
110                &self,
111                id: &gix_hash::oid,
112                buffer: &'a mut Vec<u8>,
113            ) -> Result<($object_type, Option<crate::data::entry::Location>), gix_object::find::existing_iter::Error> {
114                let id = id.as_ref();
115                self.try_find(id, buffer)
116                    .map_err(gix_object::find::existing_iter::Error::Find)?
117                    .ok_or_else(|| gix_object::find::existing_iter::Error::NotFound {
118                        oid: id.as_ref().to_owned(),
119                    })
120                    .and_then(|(o, l)| {
121                        o.$into_iter()
122                            .ok_or_else(|| gix_object::find::existing_iter::Error::ObjectKind {
123                                oid: id.to_owned(),
124                                actual: o.kind,
125                                expected: $object_kind,
126                            })
127                            .map(|i| (i, l))
128                    })
129            }
130        };
131    }
132
133    /// An extension trait with convenience functions.
134    pub trait FindExt: super::Find {
135        /// Like [`try_find(…)`][super::Find::try_find()], but flattens the `Result<Option<_>>` into a single `Result` making a non-existing object an error.
136        fn find<'a>(
137            &self,
138            id: &gix_hash::oid,
139            buffer: &'a mut Vec<u8>,
140        ) -> Result<(gix_object::Data<'a>, Option<crate::data::entry::Location>), gix_object::find::existing::Error>
141        {
142            self.try_find(id, buffer)
143                .map_err(gix_object::find::existing::Error::Find)?
144                .ok_or_else(|| gix_object::find::existing::Error::NotFound {
145                    oid: id.as_ref().to_owned(),
146                })
147        }
148
149        make_obj_lookup!(find_commit, ObjectRef::Commit, Kind::Commit, CommitRef<'a>);
150        make_obj_lookup!(find_tree, ObjectRef::Tree, Kind::Tree, TreeRef<'a>);
151        make_obj_lookup!(find_tag, ObjectRef::Tag, Kind::Tag, TagRef<'a>);
152        make_obj_lookup!(find_blob, ObjectRef::Blob, Kind::Blob, BlobRef<'a>);
153        make_iter_lookup!(find_commit_iter, Kind::Blob, CommitRefIter<'a>, try_into_commit_iter);
154        make_iter_lookup!(find_tree_iter, Kind::Tree, TreeRefIter<'a>, try_into_tree_iter);
155        make_iter_lookup!(find_tag_iter, Kind::Tag, TagRefIter<'a>, try_into_tag_iter);
156    }
157
158    impl<T: super::Find + ?Sized> FindExt for T {}
159}
160pub use ext::FindExt;
161
162mod find_impls {
163    use std::{ops::Deref, rc::Rc};
164
165    use gix_hash::oid;
166
167    use crate::{data, find};
168
169    impl<T> crate::Find for &T
170    where
171        T: crate::Find,
172    {
173        fn contains(&self, id: &oid) -> bool {
174            (*self).contains(id)
175        }
176
177        fn try_find_cached<'a>(
178            &self,
179            id: &oid,
180            buffer: &'a mut Vec<u8>,
181            pack_cache: &mut dyn crate::cache::DecodeEntry,
182        ) -> Result<Option<(gix_object::Data<'a>, Option<data::entry::Location>)>, gix_object::find::Error> {
183            (*self).try_find_cached(id, buffer, pack_cache)
184        }
185
186        fn location_by_oid(&self, id: &oid, buf: &mut Vec<u8>) -> Option<data::entry::Location> {
187            (*self).location_by_oid(id, buf)
188        }
189
190        fn pack_offsets_and_oid(&self, pack_id: u32) -> Option<Vec<(data::Offset, gix_hash::ObjectId)>> {
191            (*self).pack_offsets_and_oid(pack_id)
192        }
193
194        fn entry_by_location(&self, location: &data::entry::Location) -> Option<find::Entry> {
195            (*self).entry_by_location(location)
196        }
197    }
198
199    impl<T> super::Find for std::sync::Arc<T>
200    where
201        T: super::Find,
202    {
203        fn contains(&self, id: &oid) -> bool {
204            self.deref().contains(id)
205        }
206
207        fn try_find_cached<'a>(
208            &self,
209            id: &oid,
210            buffer: &'a mut Vec<u8>,
211            pack_cache: &mut dyn crate::cache::DecodeEntry,
212        ) -> Result<Option<(gix_object::Data<'a>, Option<data::entry::Location>)>, gix_object::find::Error> {
213            self.deref().try_find_cached(id, buffer, pack_cache)
214        }
215
216        fn location_by_oid(&self, id: &oid, buf: &mut Vec<u8>) -> Option<data::entry::Location> {
217            self.deref().location_by_oid(id, buf)
218        }
219
220        fn pack_offsets_and_oid(&self, pack_id: u32) -> Option<Vec<(data::Offset, gix_hash::ObjectId)>> {
221            self.deref().pack_offsets_and_oid(pack_id)
222        }
223
224        fn entry_by_location(&self, object: &data::entry::Location) -> Option<find::Entry> {
225            self.deref().entry_by_location(object)
226        }
227    }
228
229    impl<T> super::Find for Rc<T>
230    where
231        T: super::Find,
232    {
233        fn contains(&self, id: &oid) -> bool {
234            self.deref().contains(id)
235        }
236
237        fn try_find_cached<'a>(
238            &self,
239            id: &oid,
240            buffer: &'a mut Vec<u8>,
241            pack_cache: &mut dyn crate::cache::DecodeEntry,
242        ) -> Result<Option<(gix_object::Data<'a>, Option<data::entry::Location>)>, gix_object::find::Error> {
243            self.deref().try_find_cached(id, buffer, pack_cache)
244        }
245
246        fn location_by_oid(&self, id: &oid, buf: &mut Vec<u8>) -> Option<data::entry::Location> {
247            self.deref().location_by_oid(id, buf)
248        }
249
250        fn pack_offsets_and_oid(&self, pack_id: u32) -> Option<Vec<(data::Offset, gix_hash::ObjectId)>> {
251            self.deref().pack_offsets_and_oid(pack_id)
252        }
253
254        fn entry_by_location(&self, location: &data::entry::Location) -> Option<find::Entry> {
255            self.deref().entry_by_location(location)
256        }
257    }
258
259    impl<T> super::Find for Box<T>
260    where
261        T: super::Find,
262    {
263        fn contains(&self, id: &oid) -> bool {
264            self.deref().contains(id)
265        }
266
267        fn try_find_cached<'a>(
268            &self,
269            id: &oid,
270            buffer: &'a mut Vec<u8>,
271            pack_cache: &mut dyn crate::cache::DecodeEntry,
272        ) -> Result<Option<(gix_object::Data<'a>, Option<data::entry::Location>)>, gix_object::find::Error> {
273            self.deref().try_find_cached(id, buffer, pack_cache)
274        }
275
276        fn location_by_oid(&self, id: &oid, buf: &mut Vec<u8>) -> Option<data::entry::Location> {
277            self.deref().location_by_oid(id, buf)
278        }
279
280        fn pack_offsets_and_oid(&self, pack_id: u32) -> Option<Vec<(data::Offset, gix_hash::ObjectId)>> {
281            self.deref().pack_offsets_and_oid(pack_id)
282        }
283
284        fn entry_by_location(&self, location: &data::entry::Location) -> Option<find::Entry> {
285            self.deref().entry_by_location(location)
286        }
287    }
288}