Skip to content

Commit b941c61

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 b941c61

File tree

3 files changed

+228
-1
lines changed

3 files changed

+228
-1
lines changed

gix-object/src/find.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/// The error type returned by the [`Find`](crate::Find) and [`Header`](crate::Header) traits.
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(transparent)]
29+
Decode(crate::decode::Error),
30+
#[error("An object with id {oid} could not be found")]
31+
NotFound { oid: ObjectId },
32+
#[error("Expected object of kind {expected}")]
33+
ObjectKind { expected: crate::Kind },
34+
}
35+
}
36+
37+
///
38+
pub mod existing_iter {
39+
use gix_hash::ObjectId;
40+
41+
/// The error returned by the various [`find_*_iter()`][crate::FindExt::find_commit_iter()] trait methods.
42+
#[derive(Debug, thiserror::Error)]
43+
#[allow(missing_docs)]
44+
pub enum Error {
45+
#[error(transparent)]
46+
Find(crate::find::Error),
47+
#[error("An object with id {oid} could not be found")]
48+
NotFound { oid: ObjectId },
49+
#[error("Expected object of kind {expected}")]
50+
ObjectKind { expected: crate::Kind },
51+
}
52+
}

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: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,175 @@ 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 Rc<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::Find for Rc<T>
108+
where
109+
T: crate::Find,
110+
{
111+
fn try_find<'a>(&self, id: &oid, buffer: &'a mut Vec<u8>) -> Result<Option<Data<'a>>, crate::find::Error> {
112+
self.deref().try_find(id, buffer)
113+
}
114+
}
115+
116+
impl<T> crate::Exists for Arc<T>
117+
where
118+
T: crate::Exists,
119+
{
120+
fn exists(&self, id: &oid) -> bool {
121+
self.deref().exists(id)
122+
}
123+
}
124+
125+
impl<T> crate::Find for Arc<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+
135+
mod ext {
136+
use crate::{BlobRef, CommitRef, CommitRefIter, Kind, ObjectRef, TagRef, TagRefIter, TreeRef, TreeRefIter};
137+
138+
use crate::find;
139+
140+
macro_rules! make_obj_lookup {
141+
($method:ident, $object_variant:path, $object_kind:path, $object_type:ty) => {
142+
/// Like [`find(…)`][Self::find()], but flattens the `Result<Option<_>>` into a single `Result` making a non-existing object an error
143+
/// while returning the desired object type.
144+
fn $method<'a>(
145+
&self,
146+
id: &gix_hash::oid,
147+
buffer: &'a mut Vec<u8>,
148+
) -> Result<$object_type, find::existing_object::Error> {
149+
self.try_find(id, buffer)
150+
.map_err(find::existing_object::Error::Find)?
151+
.ok_or_else(|| find::existing_object::Error::NotFound {
152+
oid: id.as_ref().to_owned(),
153+
})
154+
.and_then(|o| o.decode().map_err(find::existing_object::Error::Decode))
155+
.and_then(|o| match o {
156+
$object_variant(o) => return Ok(o),
157+
_other => Err(find::existing_object::Error::ObjectKind {
158+
expected: $object_kind,
159+
}),
160+
})
161+
}
162+
};
163+
}
164+
165+
macro_rules! make_iter_lookup {
166+
($method:ident, $object_kind:path, $object_type:ty, $into_iter:tt) => {
167+
/// Like [`find(…)`][Self::find()], but flattens the `Result<Option<_>>` into a single `Result` making a non-existing object an error
168+
/// while returning the desired iterator type.
169+
fn $method<'a>(
170+
&self,
171+
id: &gix_hash::oid,
172+
buffer: &'a mut Vec<u8>,
173+
) -> Result<$object_type, find::existing_iter::Error> {
174+
self.try_find(id, buffer)
175+
.map_err(find::existing_iter::Error::Find)?
176+
.ok_or_else(|| find::existing_iter::Error::NotFound {
177+
oid: id.as_ref().to_owned(),
178+
})
179+
.and_then(|o| {
180+
o.$into_iter()
181+
.ok_or_else(|| find::existing_iter::Error::ObjectKind {
182+
expected: $object_kind,
183+
})
184+
})
185+
}
186+
};
187+
}
188+
189+
/// An extension trait with convenience functions.
190+
pub trait FindExt: super::Find {
191+
/// Like [`try_find(…)`][super::Find::try_find()], but flattens the `Result<Option<_>>` into a single `Result` making a non-existing object an error.
192+
fn find<'a>(
193+
&self,
194+
id: &gix_hash::oid,
195+
buffer: &'a mut Vec<u8>,
196+
) -> Result<crate::Data<'a>, find::existing::Error> {
197+
self.try_find(id, buffer)
198+
.map_err(find::existing::Error::Find)?
199+
.ok_or_else(|| find::existing::Error::NotFound { oid: id.to_owned() })
200+
}
201+
202+
make_obj_lookup!(find_commit, ObjectRef::Commit, Kind::Commit, CommitRef<'a>);
203+
make_obj_lookup!(find_tree, ObjectRef::Tree, Kind::Tree, TreeRef<'a>);
204+
make_obj_lookup!(find_tag, ObjectRef::Tag, Kind::Tag, TagRef<'a>);
205+
make_obj_lookup!(find_blob, ObjectRef::Blob, Kind::Blob, BlobRef<'a>);
206+
make_iter_lookup!(find_commit_iter, Kind::Commit, CommitRefIter<'a>, try_into_commit_iter);
207+
make_iter_lookup!(find_tree_iter, Kind::Tree, TreeRefIter<'a>, try_into_tree_iter);
208+
make_iter_lookup!(find_tag_iter, Kind::Tag, TagRefIter<'a>, try_into_tag_iter);
209+
}
210+
211+
impl<T: super::Find> FindExt for T {}
212+
}
213+
pub use ext::FindExt;
214+
}
215+
pub use find::*;

0 commit comments

Comments
 (0)