use gix_date::SecondsSinceUnixEpoch;
use smallvec::SmallVec;
use super::LazyCommit;
use crate::graph::{Commit, Either, Generation};
impl<'graph> LazyCommit<'graph> {
pub fn iter_parents(&self) -> Parents<'graph> {
let backing = match &self.backing {
Either::Left(buf) => Either::Left(gix_object::CommitRefIter::from_bytes(buf)),
Either::Right((cache, pos)) => Either::Right((*cache, cache.commit_at(*pos).iter_parents())),
};
Parents { backing }
}
pub fn committer_timestamp(&self) -> Result<SecondsSinceUnixEpoch, gix_object::decode::Error> {
Ok(match &self.backing {
Either::Left(buf) => gix_object::CommitRefIter::from_bytes(buf).committer()?.time.seconds,
Either::Right((cache, pos)) => cache.commit_at(*pos).committer_timestamp() as SecondsSinceUnixEpoch, })
}
pub fn generation(&self) -> Option<Generation> {
match &self.backing {
Either::Left(_) => None,
Either::Right((cache, pos)) => cache.commit_at(*pos).generation().into(),
}
}
pub fn to_owned<T>(&self, new_data: impl FnOnce() -> T) -> Result<Commit<T>, to_owned::Error> {
let data = new_data();
Ok(match &self.backing {
Either::Left(buf) => {
use gix_object::commit::ref_iter::Token;
let iter = gix_object::CommitRefIter::from_bytes(buf);
let mut parents = SmallVec::default();
let mut timestamp = None;
for token in iter {
match token? {
Token::Tree { .. } => {}
Token::Parent { id } => parents.push(id),
Token::Author { .. } => {}
Token::Committer { signature } => {
timestamp = Some(signature.time.seconds);
break;
}
_ => {
unreachable!(
"we break naturally after seeing the committer which is always at the same spot"
)
}
}
}
Commit {
parents,
commit_time: timestamp.unwrap_or_default(),
generation: None,
data,
}
}
Either::Right((cache, pos)) => {
let mut parents = SmallVec::default();
let commit = cache.commit_at(*pos);
for pos in commit.iter_parents() {
let pos = pos?;
parents.push(cache.commit_at(pos).id().to_owned());
}
Commit {
parents,
commit_time: commit.committer_timestamp().try_into().map_err(|_| {
to_owned::Error::CommitGraphTime {
actual: commit.committer_timestamp(),
}
})?,
generation: Some(commit.generation()),
data,
}
}
})
}
}
pub struct Parents<'graph> {
backing: Either<
gix_object::CommitRefIter<'graph>,
(
&'graph gix_commitgraph::Graph,
gix_commitgraph::file::commit::Parents<'graph>,
),
>,
}
impl<'graph> Iterator for Parents<'graph> {
type Item = Result<gix_hash::ObjectId, iter_parents::Error>;
fn next(&mut self) -> Option<Self::Item> {
match &mut self.backing {
Either::Left(it) => {
for token in it {
match token {
Ok(gix_object::commit::ref_iter::Token::Tree { .. }) => continue,
Ok(gix_object::commit::ref_iter::Token::Parent { id }) => return Some(Ok(id)),
Ok(_unused_token) => break,
Err(err) => return Some(Err(err.into())),
}
}
None
}
Either::Right((cache, it)) => it
.next()
.map(|r| r.map(|pos| cache.id_at(pos).to_owned()).map_err(Into::into)),
}
}
}
#[allow(clippy::empty_docs)]
pub mod iter_parents {
#[derive(Debug, thiserror::Error)]
#[allow(missing_docs)]
pub enum Error {
#[error("An error occurred when parsing commit parents")]
DecodeCommit(#[from] gix_object::decode::Error),
#[error("An error occurred when parsing parents from the commit graph")]
DecodeCommitGraph(#[from] gix_commitgraph::file::commit::Error),
}
}
#[allow(clippy::empty_docs)]
pub mod to_owned {
#[derive(Debug, thiserror::Error)]
#[allow(missing_docs)]
pub enum Error {
#[error("A commit could not be decoded during traversal")]
Decode(#[from] gix_object::decode::Error),
#[error("Could not find commit position in graph when traversing parents")]
CommitGraphParent(#[from] gix_commitgraph::file::commit::Error),
#[error("Commit-graph time could not be presented as signed integer: {actual}")]
CommitGraphTime { actual: u64 },
}
}