use super::DAYS_UNTIL_STALE;
use super::{signature::Signature, Repository};
use crate::error::{Error, ErrorKind};
use chrono::{DateTime, Duration, NaiveDateTime, Utc};
use git2;
#[derive(Debug)]
pub struct Commit {
pub commit_id: String,
pub author: String,
pub summary: String,
pub time: DateTime<Utc>,
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 = DateTime::from_utc(
NaiveDateTime::from_timestamp(commit.time().seconds(), 0),
Utc,
);
Ok(Commit {
commit_id,
author,
summary,
time,
signature,
signed_data,
})
}
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(())
}
pub(crate) fn ensure_fresh(&self) -> Result<(), Error> {
let fresh_after_date = Utc::now()
.checked_sub_signed(Duration::days(DAYS_UNTIL_STALE as i64))
.unwrap();
if self.time > fresh_after_date {
Ok(())
} else {
fail!(
ErrorKind::Repo,
"stale repo: not updated for {} days (last commit: {:?})",
DAYS_UNTIL_STALE,
self.time
)
}
}
}