diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/cli.rs | 46 | ||||
-rw-r--r-- | src/config.rs | 6 | ||||
-rw-r--r-- | src/error.rs | 3 | ||||
-rw-r--r-- | src/lib.rs | 13 | ||||
-rw-r--r-- | src/main.rs | 24 | ||||
-rw-r--r-- | src/projects.rs | 51 | ||||
-rw-r--r-- | src/projects/entry.rs | 18 |
7 files changed, 101 insertions, 60 deletions
@@ -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), } @@ -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, }), } } |