async_git/plumbing/
object.rs

1use std::path::PathBuf;
2
3use std::str::FromStr;
4
5use futures::io::BufReader as FBufReader;
6use tokio::{
7    fs::File,
8    io::{AsyncBufReadExt, BufReader as TBufReader},
9};
10
11use crate::errors::{Error, Result};
12use crate::plumbing::Commit;
13use crate::util::{self, FuckYouTokio, ZlibDecoder};
14
15#[derive(Debug)]
16pub struct Object {
17    pub(crate) repo_path: PathBuf,
18    pub(crate) id: Oid,
19}
20
21impl Object {
22    pub fn get_path_on_disk(&self) -> PathBuf {
23        let first_byte = format!("{:02x}", self.id.0[0]);
24        let rest = util::hexlify(&self.id.0[1..]);
25        self.repo_path.join("objects").join(first_byte).join(rest)
26    }
27
28    pub async fn peel(&self) -> Result<ParsedObject> {
29        let file = File::open(self.get_path_on_disk()).await?;
30        let fuck_you_tokio = FuckYouTokio(file);
31        let reader = FBufReader::new(fuck_you_tokio);
32        let mut r = TBufReader::new(FuckYouTokio(ZlibDecoder::new(reader)));
33
34        // we're going to determine the type of object based on the first character
35        let mut first_word = vec![];
36        r.read_until(b' ', &mut first_word).await?;
37        let first_word = String::from_utf8(first_word)?;
38
39        Ok(match first_word.as_ref() {
40            "commit " => ParsedObject::Commit(Commit::parse(r).await?),
41            _ => return Err(Error::BadGitObjectType(first_word)),
42        })
43    }
44}
45
46#[derive(Clone, Debug)]
47pub struct Oid([u8; 20]);
48
49impl FromStr for Oid {
50    type Err = Error;
51
52    fn from_str(string: &str) -> Result<Self, Self::Err> {
53        let mut buffer = [0; 20];
54        let bytes = string.as_bytes();
55
56        let len = bytes.len();
57        if len != 40 {
58            return Err(Error::ObjectIdWrongLength(len));
59        }
60
61        let byte_conv = |byte: u8| -> Result<u8, Self::Err> {
62            Ok(match byte {
63                b'0' => 0,
64                b'1' => 1,
65                b'2' => 2,
66                b'3' => 3,
67                b'4' => 4,
68                b'5' => 5,
69                b'6' => 6,
70                b'7' => 7,
71                b'8' => 8,
72                b'9' => 9,
73                b'a' => 10,
74                b'b' => 11,
75                b'c' => 12,
76                b'd' => 13,
77                b'e' => 14,
78                b'f' => 15,
79                _ => return Err(Error::InvalidHexByte(byte)),
80            })
81        };
82
83        let mut ctr = 0;
84        for window in string.as_bytes().chunks(2) {
85            if let &[first, second] = window {
86                let first_byte = byte_conv(first)?;
87                let second_byte = byte_conv(second)?;
88                buffer[ctr] = first_byte << 4 | second_byte;
89                ctr += 1;
90            }
91        }
92
93        Ok(Oid(buffer))
94    }
95}
96
97#[derive(Debug)]
98pub enum ParsedObject {
99    Blob,
100    Commit(Commit),
101    Tag,
102    Tree,
103}