diff options
author | Toby Vincent <tobyv13@gmail.com> | 2022-11-22 20:21:36 -0600 |
---|---|---|
committer | Toby Vincent <tobyv13@gmail.com> | 2022-11-22 20:22:58 -0600 |
commit | 0f428974b2d7f4cf59490904564e52d134b9ef3a (patch) | |
tree | 9e29ce643d8f1e6352daa38e4476472a1cf59ba0 | |
parent | 8c4e03340a39a966a06bdce0c948b5462774d020 (diff) |
feat: add filtering and sorting for git repos
-rw-r--r-- | Cargo.lock | 134 | ||||
-rw-r--r-- | Cargo.toml | 1 | ||||
-rw-r--r-- | src/lib.rs | 13 | ||||
-rw-r--r-- | src/main.rs | 22 | ||||
-rw-r--r-- | src/projects.rs | 21 | ||||
-rw-r--r-- | src/projects/entry.rs | 9 | ||||
-rw-r--r-- | src/sorter.rs | 62 |
7 files changed, 235 insertions, 27 deletions
@@ -130,6 +130,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" [[package]] +name = "cc" +version = "1.0.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4" +dependencies = [ + "jobserver", +] + +[[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -295,6 +304,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] +name = "form_urlencoded" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +dependencies = [ + "percent-encoding", +] + +[[package]] name = "fs2" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -352,6 +370,19 @@ dependencies = [ ] [[package]] +name = "git2" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2994bee4a3a6a51eb90c218523be382fd7ea09b16380b9312e9dbe955ff7c7d1" +dependencies = [ + "bitflags", + "libc", + "libgit2-sys", + "log", + "url", +] + +[[package]] name = "globset" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -386,6 +417,16 @@ dependencies = [ ] [[package]] +name = "idna" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] name = "ignore" version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -429,6 +470,15 @@ dependencies = [ ] [[package]] +name = "jobserver" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b" +dependencies = [ + "libc", +] + +[[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -441,6 +491,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" [[package]] +name = "libgit2-sys" +version = "0.14.0+1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47a00859c70c8a4f7218e6d1cc32875c4b55f6799445b842b0d8ed5e4c3d959b" +dependencies = [ + "cc", + "libc", + "libz-sys", + "pkg-config", +] + +[[package]] +name = "libz-sys" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9702761c3935f8cc2f101793272e202c72b99da8f4224a19ddcf1279a6450bbf" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] name = "lock_api" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -598,12 +672,24 @@ dependencies = [ ] [[package]] +name = "percent-encoding" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" + +[[package]] name = "pin-project-lite" version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" [[package]] +name = "pkg-config" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" + +[[package]] name = "plain_path" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -678,6 +764,7 @@ dependencies = [ "clap", "dirs 4.0.0", "figment", + "git2", "ignore", "pretty_assertions", "serde", @@ -902,6 +989,21 @@ dependencies = [ ] [[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] name = "tmux_interface" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -988,18 +1090,50 @@ dependencies = [ ] [[package]] +name = "unicode-bidi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" + +[[package]] name = "unicode-ident" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" [[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "url" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] name = "valuable" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] name = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -14,6 +14,7 @@ anyhow = "1.0.66" clap = { version = "4.0.18", features = ["derive", "env"] } dirs = "4.0.0" figment = { version = "0.10.8", features = ["toml", "env", "test"] } +git2 = { version = "0.15.0", default-features = false } ignore = "0.4.18" pretty_assertions = "1.3.0" serde = { version = "1.0.147", features = ["derive"] } @@ -1,20 +1,11 @@ -use anyhow::Context; - pub use crate::cli::Cli; pub use crate::config::Config; pub use crate::error::{Error, Result}; pub use crate::projects::{Projects, SearchPath}; +pub use crate::sorter::{GitSorter, Sorter}; mod cli; mod config; mod error; mod projects; - -#[tracing::instrument] -pub fn run(config: &Config) -> Result<()> { - Projects::from_provider(config) - .context("Failed to extract paths config")? - .for_each(|path| println!("{}", path.to_string_lossy())); - - Ok(()) -} +mod sorter; diff --git a/src/main.rs b/src/main.rs index b2bc844..2c2f980 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,8 @@ use anyhow::{Context, Result}; use clap::Parser; use figment::providers::{Env, Format, Toml}; -use projectr::{Cli, Config}; -use tracing_subscriber::EnvFilter; +use projectr::{Cli, Config, GitSorter, Projects, Sorter}; +use std::path::PathBuf; #[tracing::instrument] fn main() -> Result<()> { @@ -19,8 +19,22 @@ fn main() -> Result<()> { .pretty() .with_writer(std::io::stderr) .with_max_level(cli.verbosity) - .with_env_filter(EnvFilter::from_default_env()) .init(); - projectr::run(&config).context("Failed to run projectr") + run(&config).context("Failed to run projectr") +} + +#[tracing::instrument] +pub fn run(config: &Config) -> Result<()> { + let mut projects: Vec<PathBuf> = Projects::from_provider(config) + .context("Failed to extract paths config")? + .collect(); + + GitSorter::sort(&mut projects); + + for project in projects { + println!("{}", project.to_string_lossy()) + } + + Ok(()) } diff --git a/src/projects.rs b/src/projects.rs index 23530a7..8819628 100644 --- a/src/projects.rs +++ b/src/projects.rs @@ -2,7 +2,7 @@ use crate::{Config, Result}; use figment::Provider; use ignore::Walk; use std::{path::PathBuf, vec::IntoIter}; -use tracing::warn; +use tracing::{error, info}; pub use entry::SearchPath; @@ -54,16 +54,15 @@ impl Iterator for Projects { #[tracing::instrument] fn next(&mut self) -> Option<Self::Item> { - if self.walk.is_none() { - self.walk = Some(self.search_path_iter.next()?.into()); - }; - - match self.walk.as_mut()?.next()? { - Ok(d) => Some(d.into_path()), - Err(err) => { - warn!("{:?}", err); - None - } + loop { + match self.walk.as_mut().and_then(|iter| iter.next()) { + Some(Ok(path)) if SearchPath::filter(&path) => return Some(path.into_path()), + Some(Ok(path)) => info!(?path, "Ignoring filtered path"), + Some(Err(err)) => error!(%err, "Ignoring errored path"), + None => { + self.walk = Some(self.search_path_iter.next()?.into()); + } + }; } } } diff --git a/src/projects/entry.rs b/src/projects/entry.rs index 08ed07a..739ed83 100644 --- a/src/projects/entry.rs +++ b/src/projects/entry.rs @@ -1,4 +1,4 @@ -use ignore::{Walk, WalkBuilder}; +use ignore::{DirEntry, Walk, WalkBuilder}; use serde::{Deserialize, Deserializer, Serialize}; use std::{convert::Infallible, path::PathBuf, str::FromStr}; @@ -10,6 +10,12 @@ pub struct SearchPath { pub max_depth: Option<usize>, } +impl SearchPath { + pub fn filter(dir_entry: &DirEntry) -> bool { + dir_entry.path().join(".git").exists() + } +} + impl From<PathBuf> for SearchPath { fn from(path: PathBuf) -> Self { Self { @@ -25,6 +31,7 @@ impl From<SearchPath> for Walk { .standard_filters(true) .max_depth(value.max_depth) .hidden(!value.hidden) + .filter_entry(SearchPath::filter) .build() } } diff --git a/src/sorter.rs b/src/sorter.rs new file mode 100644 index 0000000..049b8d2 --- /dev/null +++ b/src/sorter.rs @@ -0,0 +1,62 @@ +use std::{cmp::Ordering, path::PathBuf}; + +use git2::{BranchType, Repository}; +use tracing::error; + +pub trait Sorter { + #[allow(clippy::ptr_arg)] + fn compare(a: &PathBuf, b: &PathBuf) -> Ordering; + + fn sort(vec: &mut Vec<PathBuf>) { + vec.sort_unstable_by(Self::compare); + } +} + +pub struct GitSorter; + +impl GitSorter { + fn get_commit(path: &PathBuf) -> Result<i64, git2::Error> { + let repo = Repository::open(path)?; + let mut branches = repo.branches(Some(BranchType::Local))?; + 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"))?; + + repo.revparse_single(name)? + .peel_to_commit() + .map(|c| c.time().seconds().max(latest)) + }) + } +} + +impl Sorter for GitSorter { + fn compare(path_a: &PathBuf, path_b: &PathBuf) -> Ordering { + let commit_a = Self::get_commit(path_a); + let commit_b = Self::get_commit(path_b); + + match (commit_a, commit_b) { + (Ok(a), Ok(b)) => a.cmp(&b), + (Ok(_), Err(error_b)) => { + error!(?path_b, ?error_b, "Error while comparing git repos"); + Ordering::Less + } + (Err(error_a), Ok(_)) => { + error!(?path_a, ?error_a, "Error while comparing git repos"); + Ordering::Greater + } + (Err(error_a), Err(error_b)) => { + error!( + ?path_a, + ?error_a, + ?path_b, + ?error_b, + "Error while comparing git repos" + ); + Ordering::Equal + } + } + } +} |