summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock134
-rw-r--r--Cargo.toml1
-rw-r--r--src/lib.rs13
-rw-r--r--src/main.rs22
-rw-r--r--src/projects.rs21
-rw-r--r--src/projects/entry.rs9
-rw-r--r--src/sorter.rs62
7 files changed, 235 insertions, 27 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 23fe0a5..c4c6e05 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -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"
diff --git a/Cargo.toml b/Cargo.toml
index 5ae07dc..66cfd99 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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"] }
diff --git a/src/lib.rs b/src/lib.rs
index d58b81b..dffa16c 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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
+ }
+ }
+ }
+}