Sebastian Thielcleanup199583a4 years agoHistory
292 lines · 11.5 KB · Rust
RawDownload
use crate::{data, find}; /// Describe how object can be located in an object store with built-in facilities to supports packs specifically.////// ## 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/44265pub 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<git_hash::oid>) -> bool;     /// Find an object matching `id` in the database while placing its raw, undecoded data into `buffer`.    /// A `pack_cache` can be used to speed up subsequent lookups, set it to [`crate::cache::Never`] if the    /// workload isn't suitable for caching.    ///    /// 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<git_hash::oid>,        buffer: &'a mut Vec<u8>,    ) -> Result<Option<(git_object::Data<'a>, Option<data::entry::Location>)>, Self::Error> {        self.try_find_cached(id, buffer, &mut crate::cache::Never)    }     /// Like [`Find::try_find()`], but with support for controlling the pack cache.    /// A `pack_cache` can be used to speed up subsequent lookups, set it to [`crate::cache::Never`] if the    /// workload isn't suitable for caching.    ///    /// Returns `Some` object if it was present in the database, or the error that occurred during lookup or object    /// retrieval.    fn try_find_cached<'a>(        &self,        id: impl AsRef<git_hash::oid>,        buffer: &'a mut Vec<u8>,        pack_cache: &mut impl crate::cache::DecodeEntry,    ) -> Result<Option<(git_object::Data<'a>, Option<data::entry::Location>)>, Self::Error>;     /// Find the packs location where an object with `id` can be found in the database, or `None` if there is no pack    /// holding the object.    ///    /// _Note_ that this is always None if the object isn't packed.    fn location_by_oid(&self, id: impl AsRef<git_hash::oid>, buf: &mut Vec<u8>) -> Option<data::entry::Location>;     /// Obtain a vector of all offsets, in index order, along with their object id.    fn pack_offsets_and_oid(&self, pack_id: u32) -> Option<Vec<(data::Offset, git_hash::ObjectId)>>;     /// Return the [`find::Entry`] for `location` if it is backed by a pack.    ///    /// Note that this is only in the interest of avoiding duplicate work during pack generation.    /// Pack locations can be obtained from [`Find::try_find()`].    ///    /// # Notes    ///    /// Custom implementations might be interested in providing their own meta-data with `object`,    /// which currently isn't possible as the `Locate` trait requires GATs to work like that.    fn entry_by_location(&self, location: &data::entry::Location) -> Option<find::Entry>;} mod ext {    use git_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<git_hash::oid>,                buffer: &'a mut Vec<u8>,            ) -> Result<($object_type, Option<crate::data::entry::Location>), 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, l)| {                        o.decode()                            .map_err(find::existing_object::Error::Decode)                            .map(|o| (o, l))                    })                    .and_then(|(o, l)| match o {                        $object_variant(o) => return Ok((o, l)),                        _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<git_hash::oid>,                buffer: &'a mut Vec<u8>,            ) -> Result<($object_type, Option<crate::data::entry::Location>), 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, l)| {                        o.$into_iter()                            .ok_or_else(|| find::existing_iter::Error::ObjectKind {                                expected: $object_kind,                            })                            .map(|i| (i, l))                    })            }        };    }     /// 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<git_hash::oid>,            buffer: &'a mut Vec<u8>,        ) -> Result<(git_object::Data<'a>, Option<crate::data::entry::Location>), 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.as_ref().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::Blob, 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; mod find_impls {    use std::{ops::Deref, rc::Rc};     use git_hash::oid;     use crate::{data, find};     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_cached<'a>(            &self,            id: impl AsRef<oid>,            buffer: &'a mut Vec<u8>,            pack_cache: &mut impl crate::cache::DecodeEntry,        ) -> Result<Option<(git_object::Data<'a>, Option<data::entry::Location>)>, Self::Error> {            (*self).try_find_cached(id, buffer, pack_cache)        }         fn location_by_oid(&self, id: impl AsRef<oid>, buf: &mut Vec<u8>) -> Option<data::entry::Location> {            (*self).location_by_oid(id, buf)        }         fn pack_offsets_and_oid(&self, pack_id: u32) -> Option<Vec<(data::Offset, git_hash::ObjectId)>> {            (*self).pack_offsets_and_oid(pack_id)        }         fn entry_by_location(&self, location: &data::entry::Location) -> Option<find::Entry> {            (*self).entry_by_location(location)        }    }     impl<T> super::Find for std::sync::Arc<T>    where        T: super::Find,    {        type Error = T::Error;         fn contains(&self, id: impl AsRef<oid>) -> bool {            self.deref().contains(id)        }         fn try_find_cached<'a>(            &self,            id: impl AsRef<oid>,            buffer: &'a mut Vec<u8>,            pack_cache: &mut impl crate::cache::DecodeEntry,        ) -> Result<Option<(git_object::Data<'a>, Option<data::entry::Location>)>, Self::Error> {            self.deref().try_find_cached(id, buffer, pack_cache)        }         fn location_by_oid(&self, id: impl AsRef<oid>, buf: &mut Vec<u8>) -> Option<data::entry::Location> {            self.deref().location_by_oid(id, buf)        }         fn pack_offsets_and_oid(&self, pack_id: u32) -> Option<Vec<(data::Offset, git_hash::ObjectId)>> {            self.deref().pack_offsets_and_oid(pack_id)        }         fn entry_by_location(&self, object: &data::entry::Location) -> Option<find::Entry> {            self.deref().entry_by_location(object)        }    }     impl<T> super::Find for Rc<T>    where        T: super::Find,    {        type Error = T::Error;         fn contains(&self, id: impl AsRef<oid>) -> bool {            self.deref().contains(id)        }         fn try_find_cached<'a>(            &self,            id: impl AsRef<oid>,            buffer: &'a mut Vec<u8>,            pack_cache: &mut impl crate::cache::DecodeEntry,        ) -> Result<Option<(git_object::Data<'a>, Option<data::entry::Location>)>, Self::Error> {            self.deref().try_find_cached(id, buffer, pack_cache)        }         fn location_by_oid(&self, id: impl AsRef<oid>, buf: &mut Vec<u8>) -> Option<data::entry::Location> {            self.deref().location_by_oid(id, buf)        }         fn pack_offsets_and_oid(&self, pack_id: u32) -> Option<Vec<(data::Offset, git_hash::ObjectId)>> {            self.deref().pack_offsets_and_oid(pack_id)        }         fn entry_by_location(&self, location: &data::entry::Location) -> Option<find::Entry> {            self.deref().entry_by_location(location)        }    }     impl<T> super::Find for Box<T>    where        T: super::Find,    {        type Error = T::Error;         fn contains(&self, id: impl AsRef<oid>) -> bool {            self.deref().contains(id)        }         fn try_find_cached<'a>(            &self,            id: impl AsRef<oid>,            buffer: &'a mut Vec<u8>,            pack_cache: &mut impl crate::cache::DecodeEntry,        ) -> Result<Option<(git_object::Data<'a>, Option<data::entry::Location>)>, Self::Error> {            self.deref().try_find_cached(id, buffer, pack_cache)        }         fn location_by_oid(&self, id: impl AsRef<oid>, buf: &mut Vec<u8>) -> Option<data::entry::Location> {            self.deref().location_by_oid(id, buf)        }         fn pack_offsets_and_oid(&self, pack_id: u32) -> Option<Vec<(data::Offset, git_hash::ObjectId)>> {            self.deref().pack_offsets_and_oid(pack_id)        }         fn entry_by_location(&self, location: &data::entry::Location) -> Option<find::Entry> {            self.deref().entry_by_location(location)        }    }}