use crate::{
error::{Error, ErrorKind},
repository::{git::Repository, signature::Signature},
};
use std::time::{Duration, SystemTime, UNIX_EPOCH};
const STALE_AFTER: Duration = Duration::from_secs(90 * 86400);
#[derive(Debug)]
pub struct Commit {
pub commit_id: String,
pub author: String,
pub summary: String,
pub timestamp: SystemTime,
pub signature: Option<Signature>,
signed_data: Option<Vec<u8>>,
}
impl Commit {
pub(crate) fn from_repo_head(repo: &Repository) -> Result<Self, Error> {
let head = repo.repo.head()?;
let oid = head.target().ok_or_else(|| {
format_err!(
ErrorKind::Repo,
"no ref target for: {}",
repo.path.display()
)
})?;
let commit_id = oid.to_string();
let commit_object = repo.repo.find_object(oid, Some(git2::ObjectType::Commit))?;
let commit = commit_object.as_commit().unwrap();
let author = commit.author().to_string();
let summary = commit
.summary()
.ok_or_else(|| format_err!(ErrorKind::Repo, "no commit summary for {}", commit_id))?
.to_owned();
let (signature, signed_data) = match repo.repo.extract_signature(&oid, None) {
Ok((ref sig, ref data)) => (
Some(Signature::from_bytes(sig)?),
Some(data.as_ref().into()),
),
_ => (None, None),
};
let time = UNIX_EPOCH + Duration::from_secs(commit.time().seconds() as u64);
Ok(Commit {
commit_id,
author,
summary,
timestamp: time,
signature,
signed_data,
})
}
pub fn is_fresh(&self) -> bool {
self.timestamp > SystemTime::now().checked_sub(STALE_AFTER).unwrap()
}
pub fn raw_signed_bytes(&self) -> Option<&[u8]> {
self.signed_data.as_ref().map(|bytes| bytes.as_ref())
}
pub(crate) fn reset(&self, repo: &Repository) -> Result<(), Error> {
let commit_object = repo.repo.find_object(
git2::Oid::from_str(&self.commit_id).unwrap(),
Some(git2::ObjectType::Commit),
)?;
repo.repo
.reset(&commit_object, git2::ResetType::Hard, None)?;
Ok(())
}
}