#![cfg_attr(
all(doc, feature = "document-features"),
doc = ::document_features::document_features!()
)]
#![cfg_attr(all(doc, feature = "document-features"), feature(doc_cfg, doc_auto_cfg))]
#![deny(missing_docs, rust_2018_idioms)]
#![forbid(unsafe_code)]
use std::borrow::Cow;
pub use bstr;
use bstr::{BStr, BString, ByteSlice};
pub use gix_date as date;
use smallvec::SmallVec;
pub mod commit;
mod object;
pub mod tag;
pub mod tree;
mod blob;
pub mod data;
pub mod find;
pub mod write {
pub type Error = Box<dyn std::error::Error + Send + Sync + 'static>;
}
mod traits;
pub use traits::{Exists, Find, FindExt, FindObjectOrHeader, Header as FindHeader, HeaderExt, Write, WriteTo};
pub mod encode;
pub(crate) mod parse;
pub mod kind;
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)]
#[allow(missing_docs)]
pub enum Kind {
Tree,
Blob,
Commit,
Tag,
}
#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct BlobRef<'a> {
pub data: &'a [u8],
}
#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Blob {
pub data: Vec<u8>,
}
#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct CommitRef<'a> {
#[cfg_attr(feature = "serde", serde(borrow))]
pub tree: &'a BStr,
pub parents: SmallVec<[&'a BStr; 1]>,
pub author: gix_actor::SignatureRef<'a>,
pub committer: gix_actor::SignatureRef<'a>,
pub encoding: Option<&'a BStr>,
pub message: &'a BStr,
pub extra_headers: Vec<(&'a BStr, Cow<'a, BStr>)>,
}
#[derive(Copy, Clone)]
pub struct CommitRefIter<'a> {
data: &'a [u8],
state: commit::ref_iter::State,
}
#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Commit {
pub tree: gix_hash::ObjectId,
pub parents: SmallVec<[gix_hash::ObjectId; 1]>,
pub author: gix_actor::Signature,
pub committer: gix_actor::Signature,
pub encoding: Option<BString>,
pub message: BString,
pub extra_headers: Vec<(BString, BString)>,
}
#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TagRef<'a> {
#[cfg_attr(feature = "serde", serde(borrow))]
pub target: &'a BStr,
pub target_kind: Kind,
pub name: &'a BStr,
pub tagger: Option<gix_actor::SignatureRef<'a>>,
pub message: &'a BStr,
pub pgp_signature: Option<&'a BStr>,
}
#[derive(Copy, Clone)]
pub struct TagRefIter<'a> {
data: &'a [u8],
state: tag::ref_iter::State,
}
#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Tag {
pub target: gix_hash::ObjectId,
pub target_kind: Kind,
pub name: BString,
pub tagger: Option<gix_actor::Signature>,
pub message: BString,
pub pgp_signature: Option<BString>,
}
#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[allow(missing_docs)]
pub enum ObjectRef<'a> {
#[cfg_attr(feature = "serde", serde(borrow))]
Tree(TreeRef<'a>),
Blob(BlobRef<'a>),
Commit(CommitRef<'a>),
Tag(TagRef<'a>),
}
#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[allow(clippy::large_enum_variant, missing_docs)]
pub enum Object {
Tree(Tree),
Blob(Blob),
Commit(Commit),
Tag(Tag),
}
#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TreeRef<'a> {
#[cfg_attr(feature = "serde", serde(borrow))]
pub entries: Vec<tree::EntryRef<'a>>,
}
#[derive(Default, PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)]
pub struct TreeRefIter<'a> {
data: &'a [u8],
}
#[derive(Default, PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Tree {
pub entries: Vec<tree::Entry>,
}
impl Tree {
pub fn empty() -> Self {
Tree { entries: Vec::new() }
}
}
#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)]
pub struct Data<'a> {
pub kind: Kind,
pub data: &'a [u8],
}
#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)]
pub struct Header {
pub kind: Kind,
pub size: u64,
}
pub mod decode {
#[cfg(feature = "verbose-object-parsing-errors")]
mod _decode {
pub type ParseError = winnow::error::ContextError<winnow::error::StrContext>;
pub(crate) fn empty_error() -> Error {
Error {
inner: winnow::error::ContextError::new(),
remaining: Default::default(),
}
}
#[derive(Debug, Clone)]
pub struct Error {
pub inner: ParseError,
pub remaining: Vec<u8>,
}
impl Error {
pub(crate) fn with_err(err: winnow::error::ErrMode<ParseError>, remaining: &[u8]) -> Self {
Self {
inner: err.into_inner().expect("we don't have streaming parsers"),
remaining: remaining.to_owned(),
}
}
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "object parsing failed at `{}`", bstr::BStr::new(&self.remaining))?;
if self.inner.context().next().is_some() {
writeln!(f)?;
self.inner.fmt(f)?;
}
Ok(())
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self.inner.cause().map(|v| v as &(dyn std::error::Error + 'static))
}
}
}
#[cfg(not(feature = "verbose-object-parsing-errors"))]
mod _decode {
pub type ParseError = ();
pub(crate) fn empty_error() -> Error {
Error { inner: () }
}
#[derive(Debug, Clone)]
pub struct Error {
pub inner: ParseError,
}
impl Error {
pub(crate) fn with_err(err: winnow::error::ErrMode<ParseError>, _remaining: &[u8]) -> Self {
Self {
inner: err.into_inner().expect("we don't have streaming parsers"),
}
}
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("object parsing failed")
}
}
impl std::error::Error for Error {}
}
pub(crate) use _decode::empty_error;
pub use _decode::{Error, ParseError};
#[derive(Debug, thiserror::Error)]
#[allow(missing_docs)]
pub enum LooseHeaderDecodeError {
#[error("{message}: {number:?}")]
ParseIntegerError {
source: gix_utils::btoi::ParseIntegerError,
message: &'static str,
number: bstr::BString,
},
#[error("{message}")]
InvalidHeader { message: &'static str },
#[error("The object header contained an unknown object kind.")]
ObjectHeader(#[from] super::kind::Error),
}
use bstr::ByteSlice;
pub fn loose_header(input: &[u8]) -> Result<(super::Kind, u64, usize), LooseHeaderDecodeError> {
use LooseHeaderDecodeError::*;
let kind_end = input.find_byte(0x20).ok_or(InvalidHeader {
message: "Expected '<type> <size>'",
})?;
let kind = super::Kind::from_bytes(&input[..kind_end])?;
let size_end = input.find_byte(0x0).ok_or(InvalidHeader {
message: "Did not find 0 byte in header",
})?;
let size_bytes = &input[kind_end + 1..size_end];
let size = gix_utils::btoi::to_signed(size_bytes).map_err(|source| ParseIntegerError {
source,
message: "Object size in header could not be parsed",
number: size_bytes.into(),
})?;
Ok((kind, size, size_end + 1))
}
}
fn object_hasher(hash_kind: gix_hash::Kind, object_kind: Kind, object_size: u64) -> gix_hash::Hasher {
let mut hasher = gix_hash::hasher(hash_kind);
hasher.update(&encode::loose_header(object_kind, object_size));
hasher
}
#[doc(alias = "hash_object", alias = "git2")]
pub fn compute_hash(
hash_kind: gix_hash::Kind,
object_kind: Kind,
data: &[u8],
) -> Result<gix_hash::ObjectId, gix_hash::hasher::Error> {
let mut hasher = object_hasher(hash_kind, object_kind, data.len() as u64);
hasher.update(data);
hasher.try_finalize()
}
#[doc(alias = "hash_file", alias = "git2")]
pub fn compute_stream_hash(
hash_kind: gix_hash::Kind,
object_kind: Kind,
stream: &mut dyn std::io::Read,
stream_len: u64,
progress: &mut dyn gix_features::progress::Progress,
should_interrupt: &std::sync::atomic::AtomicBool,
) -> Result<gix_hash::ObjectId, gix_hash::io::Error> {
let hasher = object_hasher(hash_kind, object_kind, stream_len);
gix_hash::bytes_with_hasher(stream, stream_len, hasher, progress, should_interrupt)
}