summaryrefslogtreecommitdiffstats
path: root/vendor/gix-odb/src/traits.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/gix-odb/src/traits.rs')
-rw-r--r--vendor/gix-odb/src/traits.rs310
1 files changed, 310 insertions, 0 deletions
diff --git a/vendor/gix-odb/src/traits.rs b/vendor/gix-odb/src/traits.rs
new file mode 100644
index 000000000..ddec78b8e
--- /dev/null
+++ b/vendor/gix-odb/src/traits.rs
@@ -0,0 +1,310 @@
+use std::io;
+
+use gix_object::WriteTo;
+
+/// Describe the capability to write git objects into an object store.
+pub trait Write {
+ /// The error type used for all trait methods.
+ ///
+ /// _Note_ the default implementations require the `From<io::Error>` bound.
+ type Error: std::error::Error + From<io::Error>;
+
+ /// Write objects using the intrinsic kind of [`hash`][gix_hash::Kind] into the database,
+ /// returning id to reference it in subsequent reads.
+ fn write(&self, object: impl WriteTo) -> Result<gix_hash::ObjectId, Self::Error> {
+ let mut buf = Vec::with_capacity(2048);
+ object.write_to(&mut buf)?;
+ self.write_stream(object.kind(), buf.len() as u64, buf.as_slice())
+ }
+ /// As [`write`][Write::write], but takes an [`object` kind][gix_object::Kind] along with its encoded bytes.
+ fn write_buf(&self, object: gix_object::Kind, from: &[u8]) -> Result<gix_hash::ObjectId, Self::Error> {
+ self.write_stream(object, from.len() as u64, from)
+ }
+ /// As [`write`][Write::write], but takes an input stream.
+ /// This is commonly used for writing blobs directly without reading them to memory first.
+ fn write_stream(
+ &self,
+ kind: gix_object::Kind,
+ size: u64,
+ from: impl io::Read,
+ ) -> Result<gix_hash::ObjectId, Self::Error>;
+}
+
+/// Describe how object can be located in an object store.
+///
+/// ## Notes
+///
+/// Find effectively needs [generic associated types][issue] to allow a trait for the returned object type.
+/// Until then, we will have to make due with explicit types and give them the potentially added features we want.
+///
+/// [issue]: https://github.com/rust-lang/rust/issues/44265
+pub trait Find {
+ /// The error returned by [`try_find()`][Find::try_find()]
+ type Error: std::error::Error + 'static;
+
+ /// Returns true if the object exists in the database.
+ fn contains(&self, id: impl AsRef<gix_hash::oid>) -> bool;
+
+ /// Find an object matching `id` in the database while placing its raw, possibly encoded data into `buffer`.
+ ///
+ /// Returns `Some` object if it was present in the database, or the error that occurred during lookup or object
+ /// retrieval.
+ fn try_find<'a>(
+ &self,
+ id: impl AsRef<gix_hash::oid>,
+ buffer: &'a mut Vec<u8>,
+ ) -> Result<Option<gix_object::Data<'a>>, Self::Error>;
+}
+
+/// A way to obtain object properties without fully decoding it.
+pub trait Header {
+ /// The error returned by [`try_header()`][Header::try_header()].
+ type Error: std::error::Error + 'static;
+ /// Try to read the header of the object associated with `id` or return `None` if it could not be found.
+ fn try_header(&self, id: impl AsRef<gix_hash::oid>) -> Result<Option<find::Header>, Self::Error>;
+}
+
+mod _impls {
+ use std::{io::Read, ops::Deref, rc::Rc, sync::Arc};
+
+ use gix_hash::{oid, ObjectId};
+ use gix_object::{Data, Kind, WriteTo};
+
+ use crate::find::Header;
+
+ impl<T> crate::Write for &T
+ where
+ T: crate::Write,
+ {
+ type Error = T::Error;
+
+ fn write(&self, object: impl WriteTo) -> Result<ObjectId, Self::Error> {
+ (*self).write(object)
+ }
+
+ fn write_buf(&self, object: Kind, from: &[u8]) -> Result<ObjectId, Self::Error> {
+ (*self).write_buf(object, from)
+ }
+
+ fn write_stream(&self, kind: Kind, size: u64, from: impl Read) -> Result<ObjectId, Self::Error> {
+ (*self).write_stream(kind, size, from)
+ }
+ }
+
+ impl<T> crate::Write for Arc<T>
+ where
+ T: crate::Write,
+ {
+ type Error = T::Error;
+
+ fn write(&self, object: impl WriteTo) -> Result<ObjectId, Self::Error> {
+ self.deref().write(object)
+ }
+
+ fn write_buf(&self, object: Kind, from: &[u8]) -> Result<ObjectId, Self::Error> {
+ self.deref().write_buf(object, from)
+ }
+
+ fn write_stream(&self, kind: Kind, size: u64, from: impl Read) -> Result<ObjectId, Self::Error> {
+ self.deref().write_stream(kind, size, from)
+ }
+ }
+
+ impl<T> crate::Write for Rc<T>
+ where
+ T: crate::Write,
+ {
+ type Error = T::Error;
+
+ fn write(&self, object: impl WriteTo) -> Result<ObjectId, Self::Error> {
+ self.deref().write(object)
+ }
+
+ fn write_buf(&self, object: Kind, from: &[u8]) -> Result<ObjectId, Self::Error> {
+ self.deref().write_buf(object, from)
+ }
+
+ fn write_stream(&self, kind: Kind, size: u64, from: impl Read) -> Result<ObjectId, Self::Error> {
+ self.deref().write_stream(kind, size, from)
+ }
+ }
+
+ impl<T> crate::Find for &T
+ where
+ T: crate::Find,
+ {
+ type Error = T::Error;
+
+ fn contains(&self, id: impl AsRef<oid>) -> bool {
+ (*self).contains(id)
+ }
+
+ fn try_find<'a>(&self, id: impl AsRef<oid>, buffer: &'a mut Vec<u8>) -> Result<Option<Data<'a>>, Self::Error> {
+ (*self).try_find(id, buffer)
+ }
+ }
+
+ impl<T> crate::Header for &T
+ where
+ T: crate::Header,
+ {
+ type Error = T::Error;
+
+ fn try_header(&self, id: impl AsRef<oid>) -> Result<Option<Header>, Self::Error> {
+ (*self).try_header(id)
+ }
+ }
+
+ impl<T> crate::Find for Rc<T>
+ where
+ T: crate::Find,
+ {
+ type Error = T::Error;
+
+ fn contains(&self, id: impl AsRef<oid>) -> bool {
+ self.deref().contains(id)
+ }
+
+ fn try_find<'a>(&self, id: impl AsRef<oid>, buffer: &'a mut Vec<u8>) -> Result<Option<Data<'a>>, Self::Error> {
+ self.deref().try_find(id, buffer)
+ }
+ }
+
+ impl<T> crate::Header for Rc<T>
+ where
+ T: crate::Header,
+ {
+ type Error = T::Error;
+
+ fn try_header(&self, id: impl AsRef<oid>) -> Result<Option<Header>, Self::Error> {
+ self.deref().try_header(id)
+ }
+ }
+
+ impl<T> crate::Find for Arc<T>
+ where
+ T: crate::Find,
+ {
+ type Error = T::Error;
+
+ fn contains(&self, id: impl AsRef<oid>) -> bool {
+ self.deref().contains(id)
+ }
+
+ fn try_find<'a>(&self, id: impl AsRef<oid>, buffer: &'a mut Vec<u8>) -> Result<Option<Data<'a>>, Self::Error> {
+ self.deref().try_find(id, buffer)
+ }
+ }
+
+ impl<T> crate::Header for Arc<T>
+ where
+ T: crate::Header,
+ {
+ type Error = T::Error;
+
+ fn try_header(&self, id: impl AsRef<oid>) -> Result<Option<Header>, Self::Error> {
+ self.deref().try_header(id)
+ }
+ }
+}
+
+mod ext {
+ use gix_object::{BlobRef, CommitRef, CommitRefIter, Kind, ObjectRef, TagRef, TagRefIter, TreeRef, TreeRefIter};
+
+ use crate::find;
+
+ macro_rules! make_obj_lookup {
+ ($method:ident, $object_variant:path, $object_kind:path, $object_type:ty) => {
+ /// Like [`find(…)`][Self::find()], but flattens the `Result<Option<_>>` into a single `Result` making a non-existing object an error
+ /// while returning the desired object type.
+ fn $method<'a>(
+ &self,
+ id: impl AsRef<gix_hash::oid>,
+ buffer: &'a mut Vec<u8>,
+ ) -> Result<$object_type, find::existing_object::Error<Self::Error>> {
+ let id = id.as_ref();
+ self.try_find(id, buffer)
+ .map_err(find::existing_object::Error::Find)?
+ .ok_or_else(|| find::existing_object::Error::NotFound {
+ oid: id.as_ref().to_owned(),
+ })
+ .and_then(|o| o.decode().map_err(find::existing_object::Error::Decode))
+ .and_then(|o| match o {
+ $object_variant(o) => return Ok(o),
+ _other => Err(find::existing_object::Error::ObjectKind {
+ expected: $object_kind,
+ }),
+ })
+ }
+ };
+ }
+
+ macro_rules! make_iter_lookup {
+ ($method:ident, $object_kind:path, $object_type:ty, $into_iter:tt) => {
+ /// Like [`find(…)`][Self::find()], but flattens the `Result<Option<_>>` into a single `Result` making a non-existing object an error
+ /// while returning the desired iterator type.
+ fn $method<'a>(
+ &self,
+ id: impl AsRef<gix_hash::oid>,
+ buffer: &'a mut Vec<u8>,
+ ) -> Result<$object_type, find::existing_iter::Error<Self::Error>> {
+ let id = id.as_ref();
+ self.try_find(id, buffer)
+ .map_err(find::existing_iter::Error::Find)?
+ .ok_or_else(|| find::existing_iter::Error::NotFound {
+ oid: id.as_ref().to_owned(),
+ })
+ .and_then(|o| {
+ o.$into_iter()
+ .ok_or_else(|| find::existing_iter::Error::ObjectKind {
+ expected: $object_kind,
+ })
+ })
+ }
+ };
+ }
+
+ /// An extension trait with convenience functions.
+ pub trait HeaderExt: super::Header {
+ /// Like [`try_header(…)`][super::Header::try_header()], but flattens the `Result<Option<_>>` into a single `Result` making a non-existing object an error.
+ fn header(
+ &self,
+ id: impl AsRef<gix_hash::oid>,
+ ) -> Result<crate::find::Header, find::existing::Error<Self::Error>> {
+ let id = id.as_ref();
+ self.try_header(id)
+ .map_err(find::existing::Error::Find)?
+ .ok_or_else(|| find::existing::Error::NotFound { oid: id.to_owned() })
+ }
+ }
+
+ impl<T: super::Header> HeaderExt for T {}
+
+ /// An extension trait with convenience functions.
+ pub trait FindExt: super::Find {
+ /// Like [`try_find(…)`][super::Find::try_find()], but flattens the `Result<Option<_>>` into a single `Result` making a non-existing object an error.
+ fn find<'a>(
+ &self,
+ id: impl AsRef<gix_hash::oid>,
+ buffer: &'a mut Vec<u8>,
+ ) -> Result<gix_object::Data<'a>, find::existing::Error<Self::Error>> {
+ let id = id.as_ref();
+ self.try_find(id, buffer)
+ .map_err(find::existing::Error::Find)?
+ .ok_or_else(|| find::existing::Error::NotFound { oid: id.to_owned() })
+ }
+
+ make_obj_lookup!(find_commit, ObjectRef::Commit, Kind::Commit, CommitRef<'a>);
+ make_obj_lookup!(find_tree, ObjectRef::Tree, Kind::Tree, TreeRef<'a>);
+ make_obj_lookup!(find_tag, ObjectRef::Tag, Kind::Tag, TagRef<'a>);
+ make_obj_lookup!(find_blob, ObjectRef::Blob, Kind::Blob, BlobRef<'a>);
+ make_iter_lookup!(find_commit_iter, Kind::Commit, CommitRefIter<'a>, try_into_commit_iter);
+ make_iter_lookup!(find_tree_iter, Kind::Tree, TreeRefIter<'a>, try_into_tree_iter);
+ make_iter_lookup!(find_tag_iter, Kind::Tag, TagRefIter<'a>, try_into_tag_iter);
+ }
+
+ impl<T: super::Find> FindExt for T {}
+}
+pub use ext::{FindExt, HeaderExt};
+
+use crate::find;