From 46cee052d6d4b60b483ab6841af976d68d954705 Mon Sep 17 00:00:00 2001 From: Toby Vincent Date: Sat, 26 Nov 2022 16:43:14 -0600 Subject: feat: add project filtering --- src/project/git.rs | 90 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/project/path.rs | 74 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 164 insertions(+) create mode 100644 src/project/git.rs create mode 100644 src/project/path.rs (limited to 'src/project') 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, +} + +impl GitProject { + fn new(path_buf: PathBuf) -> Result { + 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 { + 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 { + 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 { + match (self.latest_commit, other.latest_commit) { + (Some(time), Some(other_time)) => time.partial_cmp(&other_time), + _ => None, + } + } +} + +impl TryFrom for GitProject { + type Error = Error; + + fn try_from(value: PathBuf) -> Result { + Self::new(value) + } +} + +impl TryFrom for GitProject { + type Error = Error; + + fn try_from(value: DirEntry) -> Result { + Self::new(value.into_path()) + } +} + +impl From for PathBuf { + fn from(value: GitProject) -> Self { + value.path_buf + } +} diff --git a/src/project/path.rs b/src/project/path.rs new file mode 100644 index 0000000..24dad9d --- /dev/null +++ b/src/project/path.rs @@ -0,0 +1,74 @@ +use ignore::DirEntry; +use std::{ + path::PathBuf, + time::{Duration, SystemTime}, +}; + +use crate::Project; + +#[derive(Debug, PartialEq, Eq, Clone, Default)] +pub struct PathProject { + path_buf: PathBuf, + timestamp: Option, +} + +impl PathProject { + fn new(path_buf: PathBuf) -> Self { + let timestamp = path_buf + .metadata() + .ok() + .and_then(|m| m.modified().ok()) + .and_then(|t| t.duration_since(SystemTime::UNIX_EPOCH).ok()); + + Self { + timestamp, + path_buf, + } + } +} + +impl Project for PathProject { + fn timestamp(&self) -> Option { + self.timestamp + } + + fn to_path_buf(&self) -> PathBuf { + self.path_buf.to_owned() + } +} + +impl TryFrom<(&String, DirEntry)> for PathProject { + type Error = String; + + fn try_from((pattern, dir_entry): (&String, DirEntry)) -> Result { + dir_entry + .path() + .join(pattern) + .exists() + .then(|| Self::from(dir_entry.to_owned())) + .ok_or_else(|| { + format!( + "Pattern `{:?}` not found in path: `{:?}`", + pattern, dir_entry + ) + }) + } +} + +impl From for PathProject { + fn from(value: PathBuf) -> Self { + Self::new(value) + } +} + +impl From for PathProject { + fn from(value: DirEntry) -> Self { + Self::new(value.into_path()) + } +} + +impl From for PathBuf { + fn from(value: PathProject) -> Self { + value.path_buf + } +} -- cgit v1.2.3-70-g09d2