1#![cfg_attr(
5 all(doc, feature = "document-features"),
6 doc = ::document_features::document_features!()
7)]
8#![cfg_attr(all(doc, feature = "document-features"), feature(doc_cfg, doc_auto_cfg))]
9#![deny(missing_docs, rust_2018_idioms)]
10#![forbid(unsafe_code)]
11
12use std::borrow::Cow;
13
14pub use bstr;
16use bstr::{BStr, BString, ByteSlice};
17pub use gix_date as date;
19use smallvec::SmallVec;
20
21pub mod commit;
23mod object;
24pub mod tag;
26pub mod tree;
28
29mod blob;
30pub mod data;
32
33pub mod find;
35
36pub mod write {
38 pub type Error = Box<dyn std::error::Error + Send + Sync + 'static>;
40}
41
42mod traits;
43pub use traits::{Exists, Find, FindExt, FindObjectOrHeader, Header as FindHeader, HeaderExt, Write, WriteTo};
44
45pub mod encode;
46pub(crate) mod parse;
47
48pub mod kind;
50
51#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
53#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)]
54#[allow(missing_docs)]
55pub enum Kind {
56 Tree,
57 Blob,
58 Commit,
59 Tag,
60}
61#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)]
63#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
64pub struct BlobRef<'a> {
65 pub data: &'a [u8],
67}
68
69#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone)]
71#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
72pub struct Blob {
73 pub data: Vec<u8>,
75}
76
77#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone)]
82#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
83pub struct CommitRef<'a> {
84 #[cfg_attr(feature = "serde", serde(borrow))]
88 pub tree: &'a BStr,
89 pub parents: SmallVec<[&'a BStr; 1]>,
91 pub author: gix_actor::SignatureRef<'a>,
95 pub committer: gix_actor::SignatureRef<'a>,
102 pub encoding: Option<&'a BStr>,
104 pub message: &'a BStr,
106 pub extra_headers: Vec<(&'a BStr, Cow<'a, BStr>)>,
108}
109
110#[derive(Copy, Clone)]
113pub struct CommitRefIter<'a> {
114 data: &'a [u8],
115 state: commit::ref_iter::State,
116}
117
118#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone)]
120#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
121pub struct Commit {
122 pub tree: gix_hash::ObjectId,
124 pub parents: SmallVec<[gix_hash::ObjectId; 1]>,
126 pub author: gix_actor::Signature,
128 pub committer: gix_actor::Signature,
133 pub encoding: Option<BString>,
135 pub message: BString,
137 pub extra_headers: Vec<(BString, BString)>,
140}
141
142#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)]
144#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
145pub struct TagRef<'a> {
146 #[cfg_attr(feature = "serde", serde(borrow))]
148 pub target: &'a BStr,
149 pub target_kind: Kind,
151 pub name: &'a BStr,
153 pub tagger: Option<gix_actor::SignatureRef<'a>>,
155 pub message: &'a BStr,
157 pub pgp_signature: Option<&'a BStr>,
159}
160
161#[derive(Copy, Clone)]
164pub struct TagRefIter<'a> {
165 data: &'a [u8],
166 state: tag::ref_iter::State,
167}
168
169#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone)]
171#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
172pub struct Tag {
173 pub target: gix_hash::ObjectId,
175 pub target_kind: Kind,
177 pub name: BString,
179 pub tagger: Option<gix_actor::Signature>,
181 pub message: BString,
183 pub pgp_signature: Option<BString>,
185}
186
187#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone)]
195#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
196#[allow(missing_docs)]
197pub enum ObjectRef<'a> {
198 #[cfg_attr(feature = "serde", serde(borrow))]
199 Tree(TreeRef<'a>),
200 Blob(BlobRef<'a>),
201 Commit(CommitRef<'a>),
202 Tag(TagRef<'a>),
203}
204
205#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone)]
214#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
215#[allow(clippy::large_enum_variant, missing_docs)]
216pub enum Object {
217 Tree(Tree),
218 Blob(Blob),
219 Commit(Commit),
220 Tag(Tag),
221}
222#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone)]
224#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
225pub struct TreeRef<'a> {
226 #[cfg_attr(feature = "serde", serde(borrow))]
230 pub entries: Vec<tree::EntryRef<'a>>,
231}
232
233#[derive(Default, PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)]
235pub struct TreeRefIter<'a> {
236 data: &'a [u8],
238}
239
240#[derive(Default, PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone)]
242#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
243pub struct Tree {
244 pub entries: Vec<tree::Entry>,
248}
249
250impl Tree {
251 pub fn empty() -> Self {
253 Tree { entries: Vec::new() }
254 }
255}
256
257#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)]
259pub struct Data<'a> {
260 pub kind: Kind,
262 pub data: &'a [u8],
264}
265
266#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)]
268pub struct Header {
269 pub kind: Kind,
271 pub size: u64,
273}
274
275pub mod decode {
277 #[cfg(feature = "verbose-object-parsing-errors")]
278 mod _decode {
279 pub type ParseError = winnow::error::ContextError<winnow::error::StrContext>;
281
282 pub(crate) fn empty_error() -> Error {
283 Error {
284 inner: winnow::error::ContextError::new(),
285 remaining: Default::default(),
286 }
287 }
288
289 #[derive(Debug, Clone)]
291 pub struct Error {
292 pub inner: ParseError,
294 pub remaining: Vec<u8>,
296 }
297
298 impl Error {
299 pub(crate) fn with_err(err: winnow::error::ErrMode<ParseError>, remaining: &[u8]) -> Self {
300 Self {
301 inner: err.into_inner().expect("we don't have streaming parsers"),
302 remaining: remaining.to_owned(),
303 }
304 }
305 }
306
307 impl std::fmt::Display for Error {
308 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
309 write!(f, "object parsing failed at `{}`", bstr::BStr::new(&self.remaining))?;
310 if self.inner.context().next().is_some() {
311 writeln!(f)?;
312 self.inner.fmt(f)?;
313 }
314 Ok(())
315 }
316 }
317
318 impl std::error::Error for Error {
319 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
320 self.inner.cause().map(|v| v as &(dyn std::error::Error + 'static))
321 }
322 }
323 }
324
325 #[cfg(not(feature = "verbose-object-parsing-errors"))]
327 mod _decode {
328 pub type ParseError = ();
330
331 pub(crate) fn empty_error() -> Error {
332 Error { inner: () }
333 }
334
335 #[derive(Debug, Clone)]
337 pub struct Error {
338 pub inner: ParseError,
340 }
341
342 impl Error {
343 pub(crate) fn with_err(err: winnow::error::ErrMode<ParseError>, _remaining: &[u8]) -> Self {
344 Self {
345 inner: err.into_inner().expect("we don't have streaming parsers"),
346 }
347 }
348 }
349
350 impl std::fmt::Display for Error {
351 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
352 f.write_str("object parsing failed")
353 }
354 }
355
356 impl std::error::Error for Error {}
357 }
358 pub(crate) use _decode::empty_error;
359 pub use _decode::{Error, ParseError};
360
361 #[derive(Debug, thiserror::Error)]
363 #[allow(missing_docs)]
364 pub enum LooseHeaderDecodeError {
365 #[error("{message}: {number:?}")]
366 ParseIntegerError {
367 source: gix_utils::btoi::ParseIntegerError,
368 message: &'static str,
369 number: bstr::BString,
370 },
371 #[error("{message}")]
372 InvalidHeader { message: &'static str },
373 #[error("The object header contained an unknown object kind.")]
374 ObjectHeader(#[from] super::kind::Error),
375 }
376
377 use bstr::ByteSlice;
378 pub fn loose_header(input: &[u8]) -> Result<(super::Kind, u64, usize), LooseHeaderDecodeError> {
383 use LooseHeaderDecodeError::*;
384 let kind_end = input.find_byte(0x20).ok_or(InvalidHeader {
385 message: "Expected '<type> <size>'",
386 })?;
387 let kind = super::Kind::from_bytes(&input[..kind_end])?;
388 let size_end = input.find_byte(0x0).ok_or(InvalidHeader {
389 message: "Did not find 0 byte in header",
390 })?;
391 let size_bytes = &input[kind_end + 1..size_end];
392 let size = gix_utils::btoi::to_signed(size_bytes).map_err(|source| ParseIntegerError {
393 source,
394 message: "Object size in header could not be parsed",
395 number: size_bytes.into(),
396 })?;
397 Ok((kind, size, size_end + 1))
398 }
399}
400
401fn object_hasher(hash_kind: gix_hash::Kind, object_kind: Kind, object_size: u64) -> gix_hash::Hasher {
402 let mut hasher = gix_hash::hasher(hash_kind);
403 hasher.update(&encode::loose_header(object_kind, object_size));
404 hasher
405}
406
407#[doc(alias = "hash_object", alias = "git2")]
409pub fn compute_hash(
410 hash_kind: gix_hash::Kind,
411 object_kind: Kind,
412 data: &[u8],
413) -> Result<gix_hash::ObjectId, gix_hash::hasher::Error> {
414 let mut hasher = object_hasher(hash_kind, object_kind, data.len() as u64);
415 hasher.update(data);
416 hasher.try_finalize()
417}
418
419#[doc(alias = "hash_file", alias = "git2")]
424pub fn compute_stream_hash(
425 hash_kind: gix_hash::Kind,
426 object_kind: Kind,
427 stream: &mut dyn std::io::Read,
428 stream_len: u64,
429 progress: &mut dyn gix_features::progress::Progress,
430 should_interrupt: &std::sync::atomic::AtomicBool,
431) -> Result<gix_hash::ObjectId, gix_hash::io::Error> {
432 let hasher = object_hasher(hash_kind, object_kind, stream_len);
433 gix_hash::bytes_with_hasher(stream, stream_len, hasher, progress, should_interrupt)
434}