use std::io::Error as IOError;
use std::str::FromStr;
use tokio::io::{AsyncBufReadExt, AsyncRead, AsyncReadExt, AsyncWrite, BufReader};
use crate::errors::{Result, Error};
use crate::plumbing::Oid;
#[derive(Clone, Debug)]
pub struct Commit {
tree: Oid,
parents: Vec<Oid>,
author: String,
committer: String,
gpgsig: Option<String>,
message: String,
}
impl Commit {
pub async fn parse<R: AsyncRead + Unpin>(r: R) -> Result<Self> {
let mut r = BufReader::new(r);
let size = {
let mut size_buf = Vec::new();
r.read_until(0, &mut size_buf).await?;
size_buf.truncate(size_buf.len() - 1);
String::from_utf8(size_buf)?.parse::<usize>()?
};
let mut tree = None;
let mut parents = Vec::new();
let mut author = None;
let mut committer = None;
let mut gpgsig = None;
let mut process_header = |line: &str| -> Result<()> {
let mut parts = line.split(" ");
let key = parts.next().unwrap();
let value = parts.collect::<Vec<_>>().join(" ");
let value = value.trim();
match key {
"tree" => tree = Some(Oid::from_str(&value)?),
"parent" => parents.push(Oid::from_str(&value)?),
"author" => author = Some(value.to_string()),
"committer" => committer = Some(value.to_string()),
"gpgsig" => gpgsig = Some(value.to_string()),
_ => return Err(Error::InvalidCommitHeader(key.to_string())),
}
Ok(())
};
let mut prev_line = String::new();
r.read_line(&mut prev_line).await?;
loop {
let mut this_line = String::new();
r.read_line(&mut this_line).await?;
if this_line.starts_with(" ") {
prev_line += &this_line[1..];
} else {
if this_line.len() == 1 && this_line.as_bytes()[0] == b'\n' {
break;
}
process_header(&prev_line)?;
prev_line = this_line;
}
}
process_header(&prev_line)?;
let mut message = String::new();
r.read_to_string(&mut message).await?;
message = message.trim().to_string();
Ok(Commit {
tree: tree.unwrap(),
parents,
author: author.unwrap(),
committer: committer.unwrap(),
gpgsig,
message,
})
}
pub async fn write<W: AsyncWrite + Unpin>(self, w: W) -> Result<(), IOError> {
Ok(())
}
}