async-git 0.0.0-squat-name

Pure-rust async implementation of git built on tokio
Documentation
use std::path::PathBuf;

use std::str::FromStr;

use futures::io::BufReader as FBufReader;
use tokio::{
    fs::File,
    io::{AsyncBufReadExt, BufReader as TBufReader},
};

use crate::errors::{Error, Result};
use crate::plumbing::Commit;
use crate::util::{self, FuckYouTokio, ZlibDecoder};

#[derive(Debug)]
pub struct Object {
    pub(crate) repo_path: PathBuf,
    pub(crate) id: Oid,
}

impl Object {
    pub fn get_path_on_disk(&self) -> PathBuf {
        let first_byte = format!("{:02x}", self.id.0[0]);
        let rest = util::hexlify(&self.id.0[1..]);
        self.repo_path.join("objects").join(first_byte).join(rest)
    }

    pub async fn peel(&self) -> Result<ParsedObject> {
        let file = File::open(self.get_path_on_disk()).await?;
        let fuck_you_tokio = FuckYouTokio(file);
        let reader = FBufReader::new(fuck_you_tokio);
        let mut r = TBufReader::new(FuckYouTokio(ZlibDecoder::new(reader)));

        // we're going to determine the type of object based on the first character
        let mut first_word = vec![];
        r.read_until(b' ', &mut first_word).await?;
        let first_word = String::from_utf8(first_word)?;

        Ok(match first_word.as_ref() {
            "commit " => ParsedObject::Commit(Commit::parse(r).await?),
            _ => return Err(Error::BadGitObjectType(first_word)),
        })
    }
}

#[derive(Clone, Debug)]
pub struct Oid([u8; 20]);

impl FromStr for Oid {
    type Err = Error;

    fn from_str(string: &str) -> Result<Self, Self::Err> {
        let mut buffer = [0; 20];
        let bytes = string.as_bytes();

        let len = bytes.len();
        if len != 40 {
            return Err(Error::ObjectIdWrongLength(len));
        }

        let byte_conv = |byte: u8| -> Result<u8, Self::Err> {
            Ok(match byte {
                b'0' => 0,
                b'1' => 1,
                b'2' => 2,
                b'3' => 3,
                b'4' => 4,
                b'5' => 5,
                b'6' => 6,
                b'7' => 7,
                b'8' => 8,
                b'9' => 9,
                b'a' => 10,
                b'b' => 11,
                b'c' => 12,
                b'd' => 13,
                b'e' => 14,
                b'f' => 15,
                _ => return Err(Error::InvalidHexByte(byte)),
            })
        };

        let mut ctr = 0;
        for window in string.as_bytes().chunks(2) {
            if let &[first, second] = window {
                let first_byte = byte_conv(first)?;
                let second_byte = byte_conv(second)?;
                buffer[ctr] = first_byte << 4 | second_byte;
                ctr += 1;
            }
        }

        Ok(Oid(buffer))
    }
}

#[derive(Debug)]
pub enum ParsedObject {
    Blob,
    Commit(Commit),
    Tag,
    Tree,
}