This repository has no description
13 kB
343 lines
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<
29 Option<(gix_object::Data<'a>, Option<data::entry::Location>)>,
30 gix_object::find::Error,
31 > {
32 self.try_find_cached(id, buffer, &mut crate::cache::Never)
33 }
34
35 /// Like [`Find::try_find()`], but with support for controlling the pack cache.
36 /// A `pack_cache` can be used to speed up subsequent lookups, set it to [`crate::cache::Never`] if the
37 /// workload isn't suitable for caching.
38 ///
39 /// Returns `Some((<object data>, <pack location if packed>))` if it was present in the database,
40 /// or the error that occurred during lookup or object retrieval.
41 fn try_find_cached<'a>(
42 &self,
43 id: &gix_hash::oid,
44 buffer: &'a mut Vec<u8>,
45 pack_cache: &mut dyn crate::cache::DecodeEntry,
46 ) -> Result<
47 Option<(gix_object::Data<'a>, Option<data::entry::Location>)>,
48 gix_object::find::Error,
49 >;
50
51 /// Find the packs location where an object with `id` can be found in the database, or `None` if there is no pack
52 /// holding the object.
53 ///
54 /// _Note_ that this is always None if the object isn't packed even though it exists as loose object.
55 fn location_by_oid(
56 &self,
57 id: &gix_hash::oid,
58 buf: &mut Vec<u8>,
59 ) -> Option<data::entry::Location>;
60
61 /// Obtain a vector of all offsets, in index order, along with their object id.
62 fn pack_offsets_and_oid(&self, pack_id: u32)
63 -> Option<Vec<(data::Offset, gix_hash::ObjectId)>>;
64
65 /// Return the [`find::Entry`] for `location` if it is backed by a pack.
66 ///
67 /// Note that this is only in the interest of avoiding duplicate work during pack generation.
68 /// Pack locations can be obtained from [`Find::try_find()`].
69 ///
70 /// # Notes
71 ///
72 /// Custom implementations might be interested in providing their own meta-data with `object`,
73 /// which currently isn't possible as the `Locate` trait requires GATs to work like that.
74 fn entry_by_location(&self, location: &data::entry::Location) -> Option<find::Entry>;
75}
76
77mod ext {
78 use gix_object::{
79 BlobRef, CommitRef, CommitRefIter, Kind, ObjectRef, TagRef, TagRefIter, TreeRef,
80 TreeRefIter,
81 };
82
83 macro_rules! make_obj_lookup {
84 ($method:ident, $object_variant:path, $object_kind:path, $object_type:ty) => {
85 /// Like [`find(…)`][Self::find()], but flattens the `Result<Option<_>>` into a single `Result` making a non-existing object an error
86 /// while returning the desired object type.
87 fn $method<'a>(
88 &self,
89 id: &gix_hash::oid,
90 buffer: &'a mut Vec<u8>,
91 ) -> Result<
92 ($object_type, Option<crate::data::entry::Location>),
93 gix_object::find::existing_object::Error,
94 > {
95 let id = id.as_ref();
96 self.try_find(id, buffer)
97 .map_err(gix_object::find::existing_object::Error::Find)?
98 .ok_or_else(|| gix_object::find::existing_object::Error::NotFound {
99 oid: id.as_ref().to_owned(),
100 })
101 .and_then(|(o, l)| {
102 o.decode()
103 .map_err(|err| gix_object::find::existing_object::Error::Decode {
104 source: err,
105 oid: id.to_owned(),
106 })
107 .map(|o| (o, l))
108 })
109 .and_then(|(o, l)| match o {
110 $object_variant(o) => return Ok((o, l)),
111 o => Err(gix_object::find::existing_object::Error::ObjectKind {
112 oid: id.to_owned(),
113 actual: o.kind(),
114 expected: $object_kind,
115 }),
116 })
117 }
118 };
119 }
120
121 macro_rules! make_iter_lookup {
122 ($method:ident, $object_kind:path, $object_type:ty, $into_iter:tt) => {
123 /// Like [`find(…)`][Self::find()], but flattens the `Result<Option<_>>` into a single `Result` making a non-existing object an error
124 /// while returning the desired iterator type.
125 fn $method<'a>(
126 &self,
127 id: &gix_hash::oid,
128 buffer: &'a mut Vec<u8>,
129 ) -> Result<
130 ($object_type, Option<crate::data::entry::Location>),
131 gix_object::find::existing_iter::Error,
132 > {
133 let id = id.as_ref();
134 self.try_find(id, buffer)
135 .map_err(gix_object::find::existing_iter::Error::Find)?
136 .ok_or_else(|| gix_object::find::existing_iter::Error::NotFound {
137 oid: id.as_ref().to_owned(),
138 })
139 .and_then(|(o, l)| {
140 o.$into_iter()
141 .ok_or_else(|| gix_object::find::existing_iter::Error::ObjectKind {
142 oid: id.to_owned(),
143 actual: o.kind,
144 expected: $object_kind,
145 })
146 .map(|i| (i, l))
147 })
148 }
149 };
150 }
151
152 /// An extension trait with convenience functions.
153 pub trait FindExt: super::Find {
154 /// Like [`try_find(…)`][super::Find::try_find()], but flattens the `Result<Option<_>>` into a single `Result` making a non-existing object an error.
155 fn find<'a>(
156 &self,
157 id: &gix_hash::oid,
158 buffer: &'a mut Vec<u8>,
159 ) -> Result<
160 (gix_object::Data<'a>, Option<crate::data::entry::Location>),
161 gix_object::find::existing::Error,
162 > {
163 self.try_find(id, buffer)
164 .map_err(gix_object::find::existing::Error::Find)?
165 .ok_or_else(|| gix_object::find::existing::Error::NotFound {
166 oid: id.as_ref().to_owned(),
167 })
168 }
169
170 make_obj_lookup!(find_commit, ObjectRef::Commit, Kind::Commit, CommitRef<'a>);
171 make_obj_lookup!(find_tree, ObjectRef::Tree, Kind::Tree, TreeRef<'a>);
172 make_obj_lookup!(find_tag, ObjectRef::Tag, Kind::Tag, TagRef<'a>);
173 make_obj_lookup!(find_blob, ObjectRef::Blob, Kind::Blob, BlobRef<'a>);
174 make_iter_lookup!(
175 find_commit_iter,
176 Kind::Blob,
177 CommitRefIter<'a>,
178 try_into_commit_iter
179 );
180 make_iter_lookup!(
181 find_tree_iter,
182 Kind::Tree,
183 TreeRefIter<'a>,
184 try_into_tree_iter
185 );
186 make_iter_lookup!(find_tag_iter, Kind::Tag, TagRefIter<'a>, try_into_tag_iter);
187 }
188
189 impl<T: super::Find + ?Sized> FindExt for T {}
190}
191pub use ext::FindExt;
192
193mod find_impls {
194 use std::{ops::Deref, rc::Rc};
195
196 use gix_hash::oid;
197
198 use crate::{data, find};
199
200 impl<T> crate::Find for &T
201 where
202 T: crate::Find,
203 {
204 fn contains(&self, id: &oid) -> bool {
205 (*self).contains(id)
206 }
207
208 fn try_find_cached<'a>(
209 &self,
210 id: &oid,
211 buffer: &'a mut Vec<u8>,
212 pack_cache: &mut dyn crate::cache::DecodeEntry,
213 ) -> Result<
214 Option<(gix_object::Data<'a>, Option<data::entry::Location>)>,
215 gix_object::find::Error,
216 > {
217 (*self).try_find_cached(id, buffer, pack_cache)
218 }
219
220 fn location_by_oid(&self, id: &oid, buf: &mut Vec<u8>) -> Option<data::entry::Location> {
221 (*self).location_by_oid(id, buf)
222 }
223
224 fn pack_offsets_and_oid(
225 &self,
226 pack_id: u32,
227 ) -> Option<Vec<(data::Offset, gix_hash::ObjectId)>> {
228 (*self).pack_offsets_and_oid(pack_id)
229 }
230
231 fn entry_by_location(&self, location: &data::entry::Location) -> Option<find::Entry> {
232 (*self).entry_by_location(location)
233 }
234 }
235
236 impl<T> super::Find for std::sync::Arc<T>
237 where
238 T: super::Find,
239 {
240 fn contains(&self, id: &oid) -> bool {
241 self.deref().contains(id)
242 }
243
244 fn try_find_cached<'a>(
245 &self,
246 id: &oid,
247 buffer: &'a mut Vec<u8>,
248 pack_cache: &mut dyn crate::cache::DecodeEntry,
249 ) -> Result<
250 Option<(gix_object::Data<'a>, Option<data::entry::Location>)>,
251 gix_object::find::Error,
252 > {
253 self.deref().try_find_cached(id, buffer, pack_cache)
254 }
255
256 fn location_by_oid(&self, id: &oid, buf: &mut Vec<u8>) -> Option<data::entry::Location> {
257 self.deref().location_by_oid(id, buf)
258 }
259
260 fn pack_offsets_and_oid(
261 &self,
262 pack_id: u32,
263 ) -> Option<Vec<(data::Offset, gix_hash::ObjectId)>> {
264 self.deref().pack_offsets_and_oid(pack_id)
265 }
266
267 fn entry_by_location(&self, object: &data::entry::Location) -> Option<find::Entry> {
268 self.deref().entry_by_location(object)
269 }
270 }
271
272 impl<T> super::Find for Rc<T>
273 where
274 T: super::Find,
275 {
276 fn contains(&self, id: &oid) -> bool {
277 self.deref().contains(id)
278 }
279
280 fn try_find_cached<'a>(
281 &self,
282 id: &oid,
283 buffer: &'a mut Vec<u8>,
284 pack_cache: &mut dyn crate::cache::DecodeEntry,
285 ) -> Result<
286 Option<(gix_object::Data<'a>, Option<data::entry::Location>)>,
287 gix_object::find::Error,
288 > {
289 self.deref().try_find_cached(id, buffer, pack_cache)
290 }
291
292 fn location_by_oid(&self, id: &oid, buf: &mut Vec<u8>) -> Option<data::entry::Location> {
293 self.deref().location_by_oid(id, buf)
294 }
295
296 fn pack_offsets_and_oid(
297 &self,
298 pack_id: u32,
299 ) -> Option<Vec<(data::Offset, gix_hash::ObjectId)>> {
300 self.deref().pack_offsets_and_oid(pack_id)
301 }
302
303 fn entry_by_location(&self, location: &data::entry::Location) -> Option<find::Entry> {
304 self.deref().entry_by_location(location)
305 }
306 }
307
308 impl<T> super::Find for Box<T>
309 where
310 T: super::Find,
311 {
312 fn contains(&self, id: &oid) -> bool {
313 self.deref().contains(id)
314 }
315
316 fn try_find_cached<'a>(
317 &self,
318 id: &oid,
319 buffer: &'a mut Vec<u8>,
320 pack_cache: &mut dyn crate::cache::DecodeEntry,
321 ) -> Result<
322 Option<(gix_object::Data<'a>, Option<data::entry::Location>)>,
323 gix_object::find::Error,
324 > {
325 self.deref().try_find_cached(id, buffer, pack_cache)
326 }
327
328 fn location_by_oid(&self, id: &oid, buf: &mut Vec<u8>) -> Option<data::entry::Location> {
329 self.deref().location_by_oid(id, buf)
330 }
331
332 fn pack_offsets_and_oid(
333 &self,
334 pack_id: u32,
335 ) -> Option<Vec<(data::Offset, gix_hash::ObjectId)>> {
336 self.deref().pack_offsets_and_oid(pack_id)
337 }
338
339 fn entry_by_location(&self, location: &data::entry::Location) -> Option<find::Entry> {
340 self.deref().entry_by_location(location)
341 }
342 }
343}