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)));
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,
}