Skip to content

Commit f984c4c

Browse files
committed
feat: Add Find and Exists trait and extension FindExt to easily find objects.
This is more convenient than having to rely on closures all the time. Note that `Contains::contains` was renamed to `Exists::exists()`
1 parent 9158ffc commit f984c4c

File tree

3 files changed

+283
-1
lines changed

3 files changed

+283
-1
lines changed

gix-object/src/find.rs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/// The error type returned by the [`Find`](crate::Find) trait.
2+
pub type Error = Box<dyn std::error::Error + Send + Sync + 'static>;
3+
///
4+
pub mod existing {
5+
use gix_hash::ObjectId;
6+
7+
/// The error returned by the [`find(…)`][crate::FindExt::find()] trait methods.
8+
#[derive(Debug, thiserror::Error)]
9+
#[allow(missing_docs)]
10+
pub enum Error {
11+
#[error(transparent)]
12+
Find(crate::find::Error),
13+
#[error("An object with id {} could not be found", .oid)]
14+
NotFound { oid: ObjectId },
15+
}
16+
}
17+
18+
///
19+
pub mod existing_object {
20+
use gix_hash::ObjectId;
21+
22+
/// The error returned by the various [`find_*()`][crate::FindExt::find_commit()] trait methods.
23+
#[derive(Debug, thiserror::Error)]
24+
#[allow(missing_docs)]
25+
pub enum Error {
26+
#[error(transparent)]
27+
Find(crate::find::Error),
28+
#[error("Could not decode object at {oid}")]
29+
Decode {
30+
oid: ObjectId,
31+
source: crate::decode::Error,
32+
},
33+
#[error("An object with id {oid} could not be found")]
34+
NotFound { oid: ObjectId },
35+
#[error("Expected object of kind {expected} but got {actual} at {oid}")]
36+
ObjectKind {
37+
oid: ObjectId,
38+
actual: crate::Kind,
39+
expected: crate::Kind,
40+
},
41+
}
42+
}
43+
44+
///
45+
pub mod existing_iter {
46+
use gix_hash::ObjectId;
47+
48+
/// The error returned by the various [`find_*_iter()`][crate::FindExt::find_commit_iter()] trait methods.
49+
#[derive(Debug, thiserror::Error)]
50+
#[allow(missing_docs)]
51+
pub enum Error {
52+
#[error(transparent)]
53+
Find(crate::find::Error),
54+
#[error("An object with id {oid} could not be found")]
55+
NotFound { oid: ObjectId },
56+
#[error("Expected object of kind {expected} but got {actual} at {oid}")]
57+
ObjectKind {
58+
oid: ObjectId,
59+
actual: crate::Kind,
60+
expected: crate::Kind,
61+
},
62+
}
63+
}
64+
65+
/// An implementation of all traits that never fails, but also never finds anything.
66+
#[derive(Debug, Copy, Clone)]
67+
pub struct Never;
68+
69+
impl super::Find for Never {
70+
fn try_find<'a>(&self, _id: &gix_hash::oid, _buffer: &'a mut Vec<u8>) -> Result<Option<crate::Data<'a>>, Error> {
71+
Ok(None)
72+
}
73+
}
74+
75+
impl super::Exists for Never {
76+
fn exists(&self, _id: &gix_hash::oid) -> bool {
77+
false
78+
}
79+
}

gix-object/src/lib.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,11 @@ mod blob;
3030
///
3131
pub mod data;
3232

33+
///
34+
pub mod find;
35+
3336
mod traits;
34-
pub use traits::WriteTo;
37+
pub use traits::{Exists, Find, FindExt, WriteTo};
3538

3639
pub mod encode;
3740
pub(crate) mod parse;

gix-object/src/traits.rs

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,203 @@ where
4141
<T as WriteTo>::size(self)
4242
}
4343
}
44+
45+
mod find {
46+
use crate::find;
47+
48+
/// Check if an object is present in an object store.
49+
pub trait Exists {
50+
/// Returns `true` if the object exists in the database.
51+
fn exists(&self, id: &gix_hash::oid) -> bool;
52+
}
53+
54+
/// Find an object in the object store.
55+
///
56+
/// ## Notes
57+
///
58+
/// Find effectively needs [generic associated types][issue] to allow a trait for the returned object type.
59+
/// Until then, we will have to make due with explicit types and give them the potentially added features we want.
60+
///
61+
/// [issue]: https://github.com/rust-lang/rust/issues/44265
62+
pub trait Find {
63+
/// Find an object matching `id` in the database while placing its raw, possibly encoded data into `buffer`.
64+
///
65+
/// Returns `Some` object if it was present in the database, or the error that occurred during lookup or object
66+
/// retrieval.
67+
fn try_find<'a>(
68+
&self,
69+
id: &gix_hash::oid,
70+
buffer: &'a mut Vec<u8>,
71+
) -> Result<Option<crate::Data<'a>>, find::Error>;
72+
}
73+
74+
mod _impls {
75+
use std::{ops::Deref, rc::Rc, sync::Arc};
76+
77+
use crate::Data;
78+
use gix_hash::oid;
79+
80+
impl<T> crate::Exists for &T
81+
where
82+
T: crate::Exists,
83+
{
84+
fn exists(&self, id: &oid) -> bool {
85+
(*self).exists(id)
86+
}
87+
}
88+
89+
impl<T> crate::Find for &T
90+
where
91+
T: crate::Find,
92+
{
93+
fn try_find<'a>(&self, id: &oid, buffer: &'a mut Vec<u8>) -> Result<Option<Data<'a>>, crate::find::Error> {
94+
(*self).try_find(id, buffer)
95+
}
96+
}
97+
98+
impl<T> crate::Exists for Box<T>
99+
where
100+
T: crate::Exists,
101+
{
102+
fn exists(&self, id: &oid) -> bool {
103+
self.deref().exists(id)
104+
}
105+
}
106+
107+
impl<T> crate::Exists for Rc<T>
108+
where
109+
T: crate::Exists,
110+
{
111+
fn exists(&self, id: &oid) -> bool {
112+
self.deref().exists(id)
113+
}
114+
}
115+
116+
impl<T> crate::Find for Rc<T>
117+
where
118+
T: crate::Find,
119+
{
120+
fn try_find<'a>(&self, id: &oid, buffer: &'a mut Vec<u8>) -> Result<Option<Data<'a>>, crate::find::Error> {
121+
self.deref().try_find(id, buffer)
122+
}
123+
}
124+
125+
impl<T> crate::Find for Box<T>
126+
where
127+
T: crate::Find,
128+
{
129+
fn try_find<'a>(&self, id: &oid, buffer: &'a mut Vec<u8>) -> Result<Option<Data<'a>>, crate::find::Error> {
130+
self.deref().try_find(id, buffer)
131+
}
132+
}
133+
134+
impl<T> crate::Exists for Arc<T>
135+
where
136+
T: crate::Exists,
137+
{
138+
fn exists(&self, id: &oid) -> bool {
139+
self.deref().exists(id)
140+
}
141+
}
142+
143+
impl<T> crate::Find for Arc<T>
144+
where
145+
T: crate::Find,
146+
{
147+
fn try_find<'a>(&self, id: &oid, buffer: &'a mut Vec<u8>) -> Result<Option<Data<'a>>, crate::find::Error> {
148+
self.deref().try_find(id, buffer)
149+
}
150+
}
151+
}
152+
153+
mod ext {
154+
use crate::{BlobRef, CommitRef, CommitRefIter, Kind, ObjectRef, TagRef, TagRefIter, TreeRef, TreeRefIter};
155+
156+
use crate::find;
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()
174+
.map_err(|err| find::existing_object::Error::Decode {
175+
source: err,
176+
oid: id.as_ref().to_owned(),
177+
})
178+
})
179+
.and_then(|o| match o {
180+
$object_variant(o) => return Ok(o),
181+
o => Err(find::existing_object::Error::ObjectKind {
182+
oid: id.as_ref().to_owned(),
183+
actual: o.kind(),
184+
expected: $object_kind,
185+
}),
186+
})
187+
}
188+
};
189+
}
190+
191+
macro_rules! make_iter_lookup {
192+
($method:ident, $object_kind:path, $object_type:ty, $into_iter:tt) => {
193+
/// Like [`find(…)`][Self::find()], but flattens the `Result<Option<_>>` into a single `Result` making a non-existing object an error
194+
/// while returning the desired iterator type.
195+
fn $method<'a>(
196+
&self,
197+
id: &gix_hash::oid,
198+
buffer: &'a mut Vec<u8>,
199+
) -> Result<$object_type, find::existing_iter::Error> {
200+
self.try_find(id, buffer)
201+
.map_err(find::existing_iter::Error::Find)?
202+
.ok_or_else(|| find::existing_iter::Error::NotFound {
203+
oid: id.as_ref().to_owned(),
204+
})
205+
.and_then(|o| {
206+
o.$into_iter()
207+
.ok_or_else(|| find::existing_iter::Error::ObjectKind {
208+
oid: id.as_ref().to_owned(),
209+
actual: o.kind,
210+
expected: $object_kind,
211+
})
212+
})
213+
}
214+
};
215+
}
216+
217+
/// An extension trait with convenience functions.
218+
pub trait FindExt: super::Find {
219+
/// Like [`try_find(…)`][super::Find::try_find()], but flattens the `Result<Option<_>>` into a single `Result` making a non-existing object an error.
220+
fn find<'a>(
221+
&self,
222+
id: &gix_hash::oid,
223+
buffer: &'a mut Vec<u8>,
224+
) -> Result<crate::Data<'a>, find::existing::Error> {
225+
self.try_find(id, buffer)
226+
.map_err(find::existing::Error::Find)?
227+
.ok_or_else(|| find::existing::Error::NotFound { oid: id.to_owned() })
228+
}
229+
230+
make_obj_lookup!(find_commit, ObjectRef::Commit, Kind::Commit, CommitRef<'a>);
231+
make_obj_lookup!(find_tree, ObjectRef::Tree, Kind::Tree, TreeRef<'a>);
232+
make_obj_lookup!(find_tag, ObjectRef::Tag, Kind::Tag, TagRef<'a>);
233+
make_obj_lookup!(find_blob, ObjectRef::Blob, Kind::Blob, BlobRef<'a>);
234+
make_iter_lookup!(find_commit_iter, Kind::Commit, CommitRefIter<'a>, try_into_commit_iter);
235+
make_iter_lookup!(find_tree_iter, Kind::Tree, TreeRefIter<'a>, try_into_tree_iter);
236+
make_iter_lookup!(find_tag_iter, Kind::Tag, TagRefIter<'a>, try_into_tag_iter);
237+
}
238+
239+
impl<T: super::Find + ?Sized> FindExt for T {}
240+
}
241+
pub use ext::FindExt;
242+
}
243+
pub use find::*;

0 commit comments

Comments
 (0)