diff options
Diffstat (limited to 'src/project/git.rs')
-rw-r--r-- | src/project/git.rs | 90 |
1 files changed, 90 insertions, 0 deletions
diff --git a/src/project/git.rs b/src/project/git.rs new file mode 100644 index 0000000..f1c4d5a --- /dev/null +++ b/src/project/git.rs @@ -0,0 +1,90 @@ +use crate::{Error, Project}; +use git2::{BranchType, Repository}; +use ignore::DirEntry; +use std::{cmp::Ordering, path::PathBuf, time::Duration}; + +#[derive(Debug, Clone)] +pub struct GitProject { + path_buf: PathBuf, + latest_commit: Option<Duration>, +} + +impl GitProject { + fn new(path_buf: PathBuf) -> Result<Self, Error> { + let repository = Repository::open(&path_buf)?; + let latest_commit = Self::latest_commit(&repository); + Ok(Self { + path_buf, + latest_commit, + }) + } + + fn latest_commit(repository: &Repository) -> Option<Duration> { + let mut branches = repository.branches(Some(BranchType::Local)).ok()?; + branches + .try_fold(0, |latest, branch| { + let branch = branch?.0; + + let name = branch + .name()? + .ok_or_else(|| git2::Error::from_str("Failed to find branch"))?; + + repository + .revparse_single(name)? + .peel_to_commit() + .map(|c| (c.time().seconds() as u64).max(latest)) + }) + .map(Duration::from_secs) + .ok() + } +} + +impl Project for GitProject { + fn timestamp(&self) -> Option<Duration> { + self.latest_commit + } + + fn to_path_buf(&self) -> PathBuf { + self.path_buf.to_owned() + } +} + +impl PartialEq for GitProject { + fn eq(&self, other: &Self) -> bool { + match (self.latest_commit, other.latest_commit) { + (Some(time), Some(other_time)) => time.eq(&other_time), + _ => false, + } + } +} + +impl PartialOrd for GitProject { + fn partial_cmp(&self, other: &Self) -> Option<Ordering> { + match (self.latest_commit, other.latest_commit) { + (Some(time), Some(other_time)) => time.partial_cmp(&other_time), + _ => None, + } + } +} + +impl TryFrom<PathBuf> for GitProject { + type Error = Error; + + fn try_from(value: PathBuf) -> Result<Self, Self::Error> { + Self::new(value) + } +} + +impl TryFrom<DirEntry> for GitProject { + type Error = Error; + + fn try_from(value: DirEntry) -> Result<Self, Self::Error> { + Self::new(value.into_path()) + } +} + +impl From<GitProject> for PathBuf { + fn from(value: GitProject) -> Self { + value.path_buf + } +} |