async_git/plumbing/
commit.rs

1use std::io::Error as IOError;
2use std::str::FromStr;
3
4use tokio::io::{AsyncBufReadExt, AsyncRead, AsyncReadExt, AsyncWrite, BufReader};
5
6use crate::errors::{Result, Error};
7use crate::plumbing::Oid;
8
9#[derive(Clone, Debug)]
10pub struct Commit {
11    tree: Oid,
12    parents: Vec<Oid>,
13    author: String,
14    committer: String,
15    gpgsig: Option<String>,
16    message: String,
17}
18
19impl Commit {
20    pub async fn parse<R: AsyncRead + Unpin>(r: R) -> Result<Self> {
21        let mut r = BufReader::new(r);
22        let size = {
23            let mut size_buf = Vec::new();
24            r.read_until(0, &mut size_buf).await?;
25            // get rid of the null byte
26            size_buf.truncate(size_buf.len() - 1);
27            String::from_utf8(size_buf)?.parse::<usize>()?
28        };
29
30        let mut tree = None;
31        let mut parents = Vec::new();
32        let mut author = None;
33        let mut committer = None;
34        let mut gpgsig = None;
35
36        let mut process_header = |line: &str| -> Result<()> {
37            let mut parts = line.split(" ");
38            let key = parts.next().unwrap();
39            let value = parts.collect::<Vec<_>>().join(" ");
40            let value = value.trim();
41            match key {
42                "tree" => tree = Some(Oid::from_str(&value)?),
43                "parent" => parents.push(Oid::from_str(&value)?),
44                "author" => author = Some(value.to_string()),
45                "committer" => committer = Some(value.to_string()),
46                "gpgsig" => gpgsig = Some(value.to_string()),
47                _ => return Err(Error::InvalidCommitHeader(key.to_string())),
48            }
49            Ok(())
50        };
51
52        let mut prev_line = String::new();
53        r.read_line(&mut prev_line).await?;
54        loop {
55            let mut this_line = String::new();
56            r.read_line(&mut this_line).await?;
57
58            if this_line.starts_with(" ") {
59                prev_line += &this_line[1..];
60            } else {
61                // TODO: there's gotta be a cleaner way to do this
62                if this_line.len() == 1 && this_line.as_bytes()[0] == b'\n' {
63                    break;
64                }
65                process_header(&prev_line)?;
66                prev_line = this_line;
67            }
68        }
69        process_header(&prev_line)?;
70
71        let mut message = String::new();
72        r.read_to_string(&mut message).await?;
73        message = message.trim().to_string();
74
75        Ok(Commit {
76            tree: tree.unwrap(),
77            parents,
78            author: author.unwrap(),
79            committer: committer.unwrap(),
80            gpgsig,
81            message,
82        })
83    }
84
85    pub async fn write<W: AsyncWrite + Unpin>(self, w: W) -> Result<(), IOError> {
86        Ok(())
87    }
88}