summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorToby Vincent <tobyv13@gmail.com>2022-11-22 15:20:18 -0600
committerToby Vincent <tobyv13@gmail.com>2022-11-22 15:20:18 -0600
commit8c4e03340a39a966a06bdce0c948b5462774d020 (patch)
tree219cd883e757ddef446f921b0cf298c48a6df24f /src
parent72e9765d58b87125bdd5a2664bbc58202bdedff7 (diff)
refactor: improve cli and config parsing
Diffstat (limited to 'src')
-rw-r--r--src/cli.rs46
-rw-r--r--src/config.rs6
-rw-r--r--src/error.rs3
-rw-r--r--src/lib.rs13
-rw-r--r--src/main.rs24
-rw-r--r--src/projects.rs51
-rw-r--r--src/projects/entry.rs18
7 files changed, 101 insertions, 60 deletions
diff --git a/src/cli.rs b/src/cli.rs
index 827aa54..a30bb7f 100644
--- a/src/cli.rs
+++ b/src/cli.rs
@@ -1,38 +1,44 @@
use crate::{Config, SearchPath};
use clap::{Args, Parser};
use figment::{value, Metadata, Profile, Provider};
-use serde::Serialize;
+use serde::{Deserialize, Serialize};
use std::path::PathBuf;
-use tracing::Level;
+use tracing::{metadata::LevelFilter, Level};
/// Tool for listing project directories.
-#[derive(Debug, Clone, Default, Parser, Serialize)]
+#[derive(Debug, Clone, Default, Parser, Serialize, Deserialize)]
#[command(author, version, about)]
-#[serde(into = "Config")]
pub struct Cli {
+ #[command(flatten)]
+ pub projects: Projects,
+
+ #[command(flatten)]
+ pub verbosity: Verbosity,
+}
+
+#[derive(Debug, Default, Clone, Args, Serialize, Deserialize)]
+#[serde(into = "Config")]
+pub struct Projects {
/// Directory to search.
///
/// Directories are searched recursively based on `--max-depth`.
- pub(crate) paths: Vec<PathBuf>,
+ paths: Vec<PathBuf>,
/// Max depth to recurse.
///
/// By default, no limit is set. Setting to 0 will only use the supplied directory.
- #[arg(short = 'd', long)]
- pub(crate) max_depth: Option<usize>,
+ #[arg(short = 'd', long, default_value = "1")]
+ max_depth: Option<usize>,
/// Recurse into hidden directories.
///
/// Traverse into hidden directories while searching. A directory is considered hidden
/// if its name starts with a `.` sign (dot). If `--max-depth` is 0, this has no effect.
#[arg(long, default_value_t)]
- pub(crate) hidden: bool,
-
- #[command(flatten)]
- pub verbose: Verbosity,
+ hidden: bool,
}
-impl Provider for Cli {
+impl Provider for Projects {
fn metadata(&self) -> Metadata {
Metadata::named("Projectr cli provider")
}
@@ -42,8 +48,8 @@ impl Provider for Cli {
}
}
-impl From<Cli> for Config {
- fn from(value: Cli) -> Self {
+impl From<Projects> for Config {
+ fn from(value: Projects) -> Self {
let paths = value
.paths
.iter()
@@ -51,7 +57,7 @@ impl From<Cli> for Config {
.map(|p| SearchPath {
path: p,
hidden: value.hidden,
- recurse: value.max_depth,
+ max_depth: value.max_depth,
})
.collect();
@@ -59,7 +65,7 @@ impl From<Cli> for Config {
}
}
-#[derive(Debug, Default, Clone, Args)]
+#[derive(Debug, Default, Clone, Args, Serialize, Deserialize)]
pub struct Verbosity {
/// Print additional information per occurrence.
///
@@ -76,7 +82,7 @@ pub struct Verbosity {
impl From<Verbosity> for Option<Level> {
fn from(value: Verbosity) -> Self {
- match value.verbose + 1 - u8::from(value.quiet) {
+ match 1 + value.verbose - u8::from(value.quiet) {
0 => None,
1 => Some(Level::ERROR),
2 => Some(Level::WARN),
@@ -86,3 +92,9 @@ impl From<Verbosity> for Option<Level> {
}
}
}
+
+impl From<Verbosity> for LevelFilter {
+ fn from(value: Verbosity) -> Self {
+ Option::<Level>::from(value).into()
+ }
+}
diff --git a/src/config.rs b/src/config.rs
index 94421f9..677b202 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -60,17 +60,17 @@ mod tests {
SearchPath {
path: "/path/to/projects".into(),
hidden: false,
- recurse: None,
+ max_depth: None,
},
SearchPath {
path: "/path/to/other_projects".into(),
hidden: true,
- recurse: Some(1),
+ max_depth: Some(1),
},
SearchPath {
path: "/path/to/another_project".into(),
hidden: false,
- recurse: Some(0),
+ max_depth: Some(0),
},
]),
}
diff --git a/src/error.rs b/src/error.rs
index 4300e8a..ae5b227 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -7,4 +7,7 @@ pub enum Error {
#[error(transparent)]
Ignore(#[from] ignore::Error),
+
+ #[error(transparent)]
+ Other(#[from] anyhow::Error),
}
diff --git a/src/lib.rs b/src/lib.rs
index aa94330..d58b81b 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,9 +1,20 @@
+use anyhow::Context;
+
pub use crate::cli::Cli;
pub use crate::config::Config;
pub use crate::error::{Error, Result};
-pub use crate::projects::{SearchPath, Projects};
+pub use crate::projects::{Projects, SearchPath};
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(())
+}
diff --git a/src/main.rs b/src/main.rs
index 80126b2..b2bc844 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,24 +1,26 @@
use anyhow::{Context, Result};
use clap::Parser;
use figment::providers::{Env, Format, Toml};
-use projectr::{Cli, Config, Projects};
+use projectr::{Cli, Config};
+use tracing_subscriber::EnvFilter;
+#[tracing::instrument]
fn main() -> Result<()> {
+ let cli = Cli::parse();
+
let config = Config::figment()
- .merge(Cli::parse())
+ .merge(&cli.projects)
.merge(Toml::file("projectr.toml"))
.merge(Env::prefixed("PROJECTR_"))
.extract()
.context("Failed to extract config")?;
- run(&config)
-}
-
-#[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()));
+ tracing_subscriber::fmt::fmt()
+ .pretty()
+ .with_writer(std::io::stderr)
+ .with_max_level(cli.verbosity)
+ .with_env_filter(EnvFilter::from_default_env())
+ .init();
- Ok(())
+ projectr::run(&config).context("Failed to run projectr")
}
diff --git a/src/projects.rs b/src/projects.rs
index 70ea891..23530a7 100644
--- a/src/projects.rs
+++ b/src/projects.rs
@@ -1,6 +1,6 @@
use crate::{Config, Result};
use figment::Provider;
-use ignore::{Walk, WalkBuilder};
+use ignore::Walk;
use std::{path::PathBuf, vec::IntoIter};
use tracing::warn;
@@ -9,8 +9,16 @@ pub use entry::SearchPath;
mod entry;
pub struct Projects {
- paths_iter: IntoIter<SearchPath>,
- iter: Option<Walk>,
+ search_path_iter: IntoIter<SearchPath>,
+ walk: Option<Walk>,
+}
+
+impl std::fmt::Debug for Projects {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("Projects")
+ .field("paths_iter", &self.search_path_iter)
+ .finish_non_exhaustive()
+ }
}
impl Projects {
@@ -29,8 +37,8 @@ impl Projects {
impl From<Vec<SearchPath>> for Projects {
fn from(value: Vec<SearchPath>) -> Self {
Self {
- paths_iter: value.into_iter(),
- iter: None,
+ search_path_iter: value.into_iter(),
+ walk: None,
}
}
}
@@ -44,23 +52,18 @@ impl From<Config> for Projects {
impl Iterator for Projects {
type Item = PathBuf;
- #[tracing::instrument(skip(self))]
+ #[tracing::instrument]
fn next(&mut self) -> Option<Self::Item> {
- loop {
- match self.iter.as_mut().and_then(|iter| iter.next()) {
- Some(Ok(d)) => return Some(d.into_path()),
- Some(Err(err)) => warn!("{:?}", err),
- None => {
- let next = self.paths_iter.next()?;
- self.iter = Some(
- WalkBuilder::new(next.path)
- .standard_filters(true)
- .max_depth(next.recurse)
- .hidden(next.hidden)
- .build(),
- );
- }
- };
+ 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
+ }
}
}
}
@@ -89,17 +92,17 @@ mod tests {
SearchPath {
path: project_dir.to_owned(),
hidden: false,
- recurse: Some(1),
+ max_depth: Some(1),
},
SearchPath {
path: project3.to_owned(),
hidden: false,
- recurse: Some(0),
+ max_depth: Some(0),
},
SearchPath {
path: project4.to_owned(),
hidden: false,
- recurse: Some(0),
+ max_depth: Some(0),
},
]));
diff --git a/src/projects/entry.rs b/src/projects/entry.rs
index 86c23b4..08ed07a 100644
--- a/src/projects/entry.rs
+++ b/src/projects/entry.rs
@@ -1,11 +1,13 @@
+use ignore::{Walk, WalkBuilder};
use serde::{Deserialize, Deserializer, Serialize};
use std::{convert::Infallible, path::PathBuf, str::FromStr};
#[derive(Debug, PartialEq, Eq, Clone, Default, Serialize)]
+#[serde(default)]
pub struct SearchPath {
pub path: PathBuf,
pub hidden: bool,
- pub recurse: Option<usize>,
+ pub max_depth: Option<usize>,
}
impl From<PathBuf> for SearchPath {
@@ -17,6 +19,16 @@ impl From<PathBuf> for SearchPath {
}
}
+impl From<SearchPath> for Walk {
+ fn from(value: SearchPath) -> Self {
+ WalkBuilder::new(value.path)
+ .standard_filters(true)
+ .max_depth(value.max_depth)
+ .hidden(!value.hidden)
+ .build()
+ }
+}
+
impl FromStr for SearchPath {
type Err = Infallible;
@@ -36,9 +48,7 @@ impl<'de> Deserialize<'de> for SearchPath {
String(String),
Struct {
path: PathBuf,
- #[serde(default)]
hidden: bool,
- #[serde(default)]
recurse: Option<usize>,
},
}
@@ -52,7 +62,7 @@ impl<'de> Deserialize<'de> for SearchPath {
} => Ok(Self {
path,
hidden,
- recurse,
+ max_depth: recurse,
}),
}
}