pub mod headers;
pub mod trailers;
mod parse;
pub use parse::ParseError;
use core::fmt;
use std::str::{self, FromStr};
use headers::{Headers, Signature};
use trailers::{OwnedTrailer, Trailer};
use crate::author::Author;
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct CommitData<Tree, Parent> {
tree: Tree,
parents: Vec<Parent>,
author: Author,
committer: Author,
headers: Headers,
message: String,
trailers: Vec<OwnedTrailer>,
}
impl<Tree, Parent> CommitData<Tree, Parent> {
pub fn new<P, I, T>(
tree: Tree,
parents: P,
author: Author,
committer: Author,
headers: Headers,
message: String,
trailers: I,
) -> Self
where
P: IntoIterator<Item = Parent>,
I: IntoIterator<Item = T>,
OwnedTrailer: From<T>,
{
let trailers = trailers.into_iter().map(OwnedTrailer::from).collect();
let parents = parents.into_iter().collect();
Self {
tree,
parents,
author,
committer,
headers,
message,
trailers,
}
}
pub fn tree(&self) -> &Tree {
&self.tree
}
pub fn parents(&self) -> impl Iterator<Item = Parent> + '_
where
Parent: Clone,
{
self.parents.iter().cloned()
}
pub fn author(&self) -> &Author {
&self.author
}
pub fn committer(&self) -> &Author {
&self.committer
}
pub fn message(&self) -> &str {
&self.message
}
pub fn signatures(&self) -> impl Iterator<Item = Signature<'_>> + '_ {
self.headers.signatures()
}
pub fn strip_signatures(mut self) -> Self {
self.headers.strip_signatures();
self
}
pub fn headers(&self) -> impl Iterator<Item = (&str, &str)> {
self.headers.iter()
}
pub fn values<'a>(&'a self, name: &'a str) -> impl Iterator<Item = &'a str> + 'a {
self.headers.values(name)
}
pub fn push_header(&mut self, name: &str, value: &str) {
self.headers.push(name, value.trim());
}
pub fn trailers(&self) -> impl Iterator<Item = &OwnedTrailer> {
self.trailers.iter()
}
pub fn map_tree<U, E, F>(self, f: F) -> Result<CommitData<U, Parent>, E>
where
F: FnOnce(Tree) -> Result<U, E>,
{
Ok(CommitData {
tree: f(self.tree)?,
parents: self.parents,
author: self.author,
committer: self.committer,
headers: self.headers,
message: self.message,
trailers: self.trailers,
})
}
pub fn map_parents<U, E, F>(self, f: F) -> Result<CommitData<Tree, U>, E>
where
F: FnMut(Parent) -> Result<U, E>,
{
Ok(CommitData {
tree: self.tree,
parents: self
.parents
.into_iter()
.map(f)
.collect::<Result<Vec<_>, _>>()?,
author: self.author,
committer: self.committer,
headers: self.headers,
message: self.message,
trailers: self.trailers,
})
}
}
impl<Tree, Parent> CommitData<Tree, Parent>
where
Tree: str::FromStr,
Parent: str::FromStr,
Tree::Err: std::error::Error + Send + Sync + 'static,
Parent::Err: std::error::Error + Send + Sync + 'static,
{
pub fn from_bytes(bytes: &[u8]) -> Result<Self, ParseError> {
let s = str::from_utf8(bytes).map_err(ParseError::Utf8)?;
parse::parse(s)
}
}
impl<Tree, Parent> FromStr for CommitData<Tree, Parent>
where
Tree: str::FromStr,
Parent: str::FromStr,
Tree::Err: std::error::Error + Send + Sync + 'static,
Parent::Err: std::error::Error + Send + Sync + 'static,
{
type Err = ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
parse::parse(s)
}
}
impl<Tree: fmt::Display, Parent: fmt::Display> fmt::Display for CommitData<Tree, Parent> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "tree {}", self.tree)?;
for parent in self.parents.iter() {
writeln!(f, "parent {parent}")?;
}
writeln!(f, "author {}", self.author)?;
writeln!(f, "committer {}", self.committer)?;
for (name, value) in self.headers.iter() {
writeln!(f, "{name} {}", value.replace('\n', "\n "))?;
}
writeln!(f)?;
write!(f, "{}", self.message.trim())?;
writeln!(f)?;
if !self.trailers.is_empty() {
writeln!(f)?;
}
for trailer in self.trailers.iter() {
writeln!(f, "{}", Trailer::from(trailer).display(": "))?;
}
Ok(())
}
}