[go: up one dir, main page]

gix-object 0.39.0

Immutable and mutable git objects with decoding and encoding support
Documentation
use std::io::Write;

use crate::Kind;

/// Writing of objects to a `Write` implementation
pub trait WriteTo {
    /// Write a representation of this instance to `out`.
    fn write_to(&self, out: &mut dyn std::io::Write) -> std::io::Result<()>;

    /// Returns the type of this object.
    fn kind(&self) -> Kind;

    /// Returns the size of this object's representation (the amount
    /// of data which would be written by [`write_to`](Self::write_to)).
    ///
    /// [`size`](Self::size)'s value has no bearing on the validity of
    /// the object, as such it's possible for [`size`](Self::size) to
    /// return a sensible value but [`write_to`](Self::write_to) to
    /// fail because the object was not actually valid in some way.
    fn size(&self) -> u64;

    /// Returns a loose object header based on the object's data
    fn loose_header(&self) -> smallvec::SmallVec<[u8; 28]> {
        crate::encode::loose_header(self.kind(), self.size())
    }
}

impl<T> WriteTo for &T
where
    T: WriteTo,
{
    fn write_to(&self, out: &mut dyn Write) -> std::io::Result<()> {
        <T as WriteTo>::write_to(self, out)
    }

    fn kind(&self) -> Kind {
        <T as WriteTo>::kind(self)
    }

    fn size(&self) -> u64 {
        <T as WriteTo>::size(self)
    }
}

mod find {
    use crate::find;

    /// Check if an object is present in an object store.
    pub trait Exists {
        /// Returns `true` if the object exists in the database.
        fn exists(&self, id: &gix_hash::oid) -> bool;
    }

    /// Find an object in the 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 {
        /// 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: &gix_hash::oid,
            buffer: &'a mut Vec<u8>,
        ) -> Result<Option<crate::Data<'a>>, find::Error>;
    }

    /// Find the header of an object in the object store.
    pub trait Header {
        /// Find the header of the object matching `id` in the database.
        ///
        /// Returns `Some` header if it was present, or the error that occurred during lookup.
        fn try_header(&self, id: &gix_hash::oid) -> Result<Option<crate::Header>, find::Error>;
    }

    /// A combination of [`Find`] and [`Header`] traits to help with `dyn` trait objects.
    pub trait FindObjectOrHeader: Find + Header {}

    mod _impls {
        use std::{ops::Deref, rc::Rc, sync::Arc};

        use gix_hash::oid;

        use crate::Data;

        impl<T> crate::Exists for &T
        where
            T: crate::Exists,
        {
            fn exists(&self, id: &oid) -> bool {
                (*self).exists(id)
            }
        }

        impl<T> crate::FindObjectOrHeader for T where T: crate::Find + crate::FindHeader {}

        impl<T> crate::Find for &T
        where
            T: crate::Find,
        {
            fn try_find<'a>(&self, id: &oid, buffer: &'a mut Vec<u8>) -> Result<Option<Data<'a>>, crate::find::Error> {
                (*self).try_find(id, buffer)
            }
        }

        impl<T> crate::FindHeader for &T
        where
            T: crate::FindHeader,
        {
            fn try_header(&self, id: &gix_hash::oid) -> Result<Option<crate::Header>, crate::find::Error> {
                (*self).try_header(id)
            }
        }

        impl<T> crate::Exists for Box<T>
        where
            T: crate::Exists,
        {
            fn exists(&self, id: &oid) -> bool {
                self.deref().exists(id)
            }
        }

        impl<T> crate::Exists for Rc<T>
        where
            T: crate::Exists,
        {
            fn exists(&self, id: &oid) -> bool {
                self.deref().exists(id)
            }
        }

        impl<T> crate::Find for Rc<T>
        where
            T: crate::Find,
        {
            fn try_find<'a>(&self, id: &oid, buffer: &'a mut Vec<u8>) -> Result<Option<Data<'a>>, crate::find::Error> {
                self.deref().try_find(id, buffer)
            }
        }

        impl<T> crate::FindHeader for Rc<T>
        where
            T: crate::FindHeader,
        {
            fn try_header(&self, id: &gix_hash::oid) -> Result<Option<crate::Header>, crate::find::Error> {
                self.deref().try_header(id)
            }
        }

        impl<T> crate::Find for Box<T>
        where
            T: crate::Find,
        {
            fn try_find<'a>(&self, id: &oid, buffer: &'a mut Vec<u8>) -> Result<Option<Data<'a>>, crate::find::Error> {
                self.deref().try_find(id, buffer)
            }
        }

        impl<T> crate::FindHeader for Box<T>
        where
            T: crate::FindHeader,
        {
            fn try_header(&self, id: &gix_hash::oid) -> Result<Option<crate::Header>, crate::find::Error> {
                self.deref().try_header(id)
            }
        }

        impl<T> crate::Exists for Arc<T>
        where
            T: crate::Exists,
        {
            fn exists(&self, id: &oid) -> bool {
                self.deref().exists(id)
            }
        }

        impl<T> crate::Find for Arc<T>
        where
            T: crate::Find,
        {
            fn try_find<'a>(&self, id: &oid, buffer: &'a mut Vec<u8>) -> Result<Option<Data<'a>>, crate::find::Error> {
                self.deref().try_find(id, buffer)
            }
        }

        impl<T> crate::FindHeader for Arc<T>
        where
            T: crate::FindHeader,
        {
            fn try_header(&self, id: &gix_hash::oid) -> Result<Option<crate::Header>, crate::find::Error> {
                self.deref().try_header(id)
            }
        }
    }

    mod ext {
        use crate::{
            find, BlobRef, CommitRef, CommitRefIter, Kind, ObjectRef, TagRef, TagRefIter, TreeRef, TreeRefIter,
        };

        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: &gix_hash::oid,
                    buffer: &'a mut Vec<u8>,
                ) -> Result<$object_type, find::existing_object::Error> {
                    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(|err| find::existing_object::Error::Decode {
                                    source: err,
                                    oid: id.as_ref().to_owned(),
                                })
                        })
                        .and_then(|o| match o {
                            $object_variant(o) => return Ok(o),
                            o => Err(find::existing_object::Error::ObjectKind {
                                oid: id.as_ref().to_owned(),
                                actual: o.kind(),
                                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: &gix_hash::oid,
                    buffer: &'a mut Vec<u8>,
                ) -> Result<$object_type, find::existing_iter::Error> {
                    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 {
                                    oid: id.as_ref().to_owned(),
                                    actual: o.kind,
                                    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 header an error.
            fn header(&self, id: &gix_hash::oid) -> Result<crate::Header, find::existing::Error> {
                self.try_header(id)
                    .map_err(find::existing::Error::Find)?
                    .ok_or_else(|| find::existing::Error::NotFound { oid: id.to_owned() })
            }
        }

        /// 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: &gix_hash::oid,
                buffer: &'a mut Vec<u8>,
            ) -> Result<crate::Data<'a>, find::existing::Error> {
                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 + ?Sized> FindExt for T {}
    }
    pub use ext::{FindExt, HeaderExt};
}
pub use find::*;