thin-fetch 0.1.0

Low-level Git crate to fetch objects with high granularity
Documentation
use std::ops::Deref;

use ring::digest::Digest;

use crate::utils::{as_hex, get_hash};
use crate::{Error, HashKind, error};

#[derive(Debug)]
pub struct Commit {
    pub hash: Digest,
    pub commit: gix_object::Commit,
}

impl Commit {
    /// Parse commit from raw data.
    ///
    /// # Errors
    ///
    /// Returns an error when the commit could not be parsed.
    pub fn parse(raw: &[u8], hash_kind: HashKind) -> Result<Self, Error> {
        let hash = get_hash("commit", raw, hash_kind);

        let cref = gix_object::CommitRef::from_bytes(raw, hash_kind.into())
            .map_err(|err| error!("could not parse commit: {err}"))?;

        let commit = cref
            .to_owned()
            .map_err(|err| error!("could not convert commit ref to owned: {err}"))?;

        Ok(Self { hash, commit })
    }

    /// Get the commit header as [`std::fmt::Display`].
    #[must_use]
    pub fn header(&self) -> CommitHeader<'_> {
        CommitHeader { commit: self }
    }
}

impl Deref for Commit {
    type Target = gix_object::Commit;

    fn deref(&self) -> &Self::Target {
        &self.commit
    }
}

pub struct CommitHeader<'a> {
    commit: &'a Commit,
}

impl std::fmt::Display for CommitHeader<'_> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let hash = as_hex(self.commit.hash.as_ref());

        writeln!(f, "commit {hash}").unwrap();

        let author = &self.commit.author;
        writeln!(f, "Author: {} <{}>", author.name, author.email)?;

        let zoned = author.time.to_zoned().unwrap();
        let date = jiff::fmt::rfc2822::to_string(&zoned).unwrap();
        writeln!(f, "Date:   {date}")?;
        writeln!(f)?;

        let msg = String::from_utf8_lossy(&self.commit.message);
        let message = msg.trim_end_matches('\n');

        for line in message.split('\n') {
            writeln!(f, "    {line}")?;
        }

        Ok(())
    }
}