diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/cli.rs | 17 | ||||
-rw-r--r-- | src/config.rs | 87 | ||||
-rw-r--r-- | src/error.rs | 10 | ||||
-rw-r--r-- | src/finder.rs | 111 | ||||
-rw-r--r-- | src/finder/config.rs | 84 | ||||
-rw-r--r-- | src/finder/error.rs | 16 | ||||
-rw-r--r-- | src/lib.rs | 8 | ||||
-rw-r--r-- | src/logging.rs | 65 | ||||
-rw-r--r-- | src/logging/config.rs | 87 | ||||
-rw-r--r-- | src/logging/error.rs | 10 | ||||
-rw-r--r-- | src/logging/level.rs | 21 | ||||
-rw-r--r-- | src/main.rs | 24 | ||||
-rw-r--r-- | src/paths/config.rs | 82 | ||||
-rw-r--r-- | src/paths/error.rs | 10 | ||||
-rw-r--r-- | src/projects.rs (renamed from src/paths.rs) | 32 | ||||
-rw-r--r-- | src/projects/entry.rs (renamed from src/paths/path_entry.rs) | 8 |
16 files changed, 61 insertions, 611 deletions
@@ -1,11 +1,11 @@ -use crate::{paths::PathEntry, Config}; +use crate::{Config, SearchPath}; use clap::{Args, Parser}; use figment::{value, Metadata, Profile, Provider}; use serde::Serialize; use std::path::PathBuf; use tracing::Level; -/// Tool for managing tmux sessions +/// Tool for listing project directories. #[derive(Debug, Clone, Default, Parser, Serialize)] #[command(author, version, about)] #[serde(into = "Config")] @@ -34,7 +34,7 @@ pub struct Cli { impl Provider for Cli { fn metadata(&self) -> Metadata { - Metadata::named("Tmuxr cli provider") + Metadata::named("Projectr cli provider") } fn data(&self) -> figment::error::Result<value::Map<Profile, value::Dict>> { @@ -48,21 +48,14 @@ impl From<Cli> for Config { .paths .iter() .cloned() - .map(|p| PathEntry { + .map(|p| SearchPath { path: p, hidden: value.hidden, recurse: value.max_depth, }) .collect(); - Config { - paths: crate::paths::Config { paths }, - logging: crate::logging::Config { - stdout: value.verbose.into(), - ..Default::default() - }, - ..Default::default() - } + Config { paths } } } diff --git a/src/config.rs b/src/config.rs index 5946102..94421f9 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,31 +1,27 @@ -use crate::{finder, logging, paths, Error, Result}; +use super::SearchPath; use figment::{providers::Serialized, value, Figment, Metadata, Profile, Provider}; use serde::{Deserialize, Serialize}; -#[derive(Debug, Default, PartialEq, Eq, Serialize, Deserialize)] -#[serde(default)] +#[derive(Debug, PartialEq, Eq, Clone, Default, Serialize, Deserialize)] pub struct Config { - #[serde(flatten)] - pub paths: paths::Config, - pub finder: finder::Config, - pub logging: logging::Config, + pub(crate) paths: Vec<SearchPath>, } impl Config { - // Provide a default provider, a `Figment`. - pub fn figment() -> Figment { - Figment::from(Config::default()) - } - // Extract the configuration from any `Provider` pub fn extract<T: Provider>(provider: T) -> figment::error::Result<Config> { Figment::from(provider).extract() } + + // Provide a default provider, a `Figment`. + pub fn figment() -> Figment { + Figment::from(Config::default()) + } } impl Provider for Config { fn metadata(&self) -> Metadata { - Metadata::named("Tmuxr directory config") + Metadata::named("Projectr config") } fn data(&self) -> figment::error::Result<value::Map<Profile, value::Dict>> { @@ -33,14 +29,6 @@ impl Provider for Config { } } -impl TryFrom<Figment> for Config { - type Error = Error; - - fn try_from(value: Figment) -> Result<Self> { - value.extract().map_err(Into::into) - } -} - #[cfg(test)] mod tests { use super::*; @@ -51,59 +39,40 @@ mod tests { fn test_extract() { figment::Jail::expect_with(|jail| { jail.create_file( - "tmuxr.toml", + "file.toml", r#" paths = [ "/path/to/projects", { path = "/path/to/other_projects", recurse = 1, hidden = true }, { path = "/path/to/another_project", recurse = 0 }, ] - - [finder] - program = "fzf" - args = ["-0", "-1", "--preview='cat'"] - - [logging] - level = "warn" - path = "/path/to/log_file" "#, )?; let config: Config = Figment::from(Serialized::defaults(Config::default())) - .merge(Toml::file("tmuxr.toml")) + .merge(Toml::file("file.toml")) .extract()?; assert_eq!( config, Config { - paths: paths::Config { - paths: Vec::from([ - paths::PathEntry { - path: "/path/to/projects".into(), - hidden: false, - recurse: None, - }, - paths::PathEntry { - path: "/path/to/other_projects".into(), - hidden: true, - recurse: Some(1), - }, - paths::PathEntry { - path: "/path/to/another_project".into(), - hidden: false, - recurse: Some(0), - }, - ]), - }, - finder: finder::Config { - program: "fzf".into(), - args: vec!["-0".into(), "-1".into(), "--preview='cat'".into()], - }, - logging: logging::Config { - stdout: None, - level: Some(logging::Level::WARN), - path: "/path/to/log_file".into() - }, + paths: Vec::from([ + SearchPath { + path: "/path/to/projects".into(), + hidden: false, + recurse: None, + }, + SearchPath { + path: "/path/to/other_projects".into(), + hidden: true, + recurse: Some(1), + }, + SearchPath { + path: "/path/to/another_project".into(), + hidden: false, + recurse: Some(0), + }, + ]), } ); diff --git a/src/error.rs b/src/error.rs index 83a2ef7..4300e8a 100644 --- a/src/error.rs +++ b/src/error.rs @@ -3,14 +3,8 @@ pub type Result<T> = std::result::Result<T, Error>; #[derive(thiserror::Error, Debug)] pub enum Error { #[error(transparent)] - Logging(#[from] crate::logging::Error), - - #[error(transparent)] - Paths(#[from] crate::paths::Error), - - #[error(transparent)] - Finder(#[from] crate::finder::Error), + Config(#[from] figment::error::Error), #[error(transparent)] - Config(#[from] figment::error::Error), + Ignore(#[from] ignore::Error), } diff --git a/src/finder.rs b/src/finder.rs deleted file mode 100644 index 3c74417..0000000 --- a/src/finder.rs +++ /dev/null @@ -1,111 +0,0 @@ -use figment::Provider; -use std::{ - ffi::OsStr, - io::Write, - ops::{Deref, DerefMut}, - os::unix::prelude::OsStrExt, - process::{Child, Command, Output, Stdio}, -}; - -pub(crate) use config::Config; -pub(crate) use error::{Error, Result}; - -mod config; -mod error; - -pub struct Finder { - program: String, - args: Vec<String>, -} - -impl Finder { - pub fn new() -> Result<Self> { - Self::from_provider(Config::figment()) - } - - /// Extract `Config` from `provider` to construct new `Finder` - pub fn from_provider<T: Provider>(provider: T) -> Result<Self> { - Config::extract(&provider) - .map(|config| Finder { - program: config.program, - args: config.args, - }) - .map_err(Into::into) - } - - pub fn spawn(self) -> Result<FinderChild> { - Command::new(self.program) - .args(self.args) - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .spawn() - .map(FinderChild) - .map_err(Into::into) - } -} - -pub struct FinderChild(Child); - -impl FinderChild { - pub fn find<V, I>(mut self, items: V) -> Result<Output> - where - V: IntoIterator<Item = I>, - I: AsRef<OsStr>, - { - let stdin = self.stdin.as_mut().ok_or(Error::Stdin)?; - - items.into_iter().try_for_each(|item| -> Result<()> { - stdin.write_all(item.as_ref().as_bytes())?; - stdin.write_all("\n".as_bytes()).map_err(From::from) - })?; - - self.0.wait_with_output().map_err(Into::into) - } -} - -impl Deref for FinderChild { - type Target = Child; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl DerefMut for FinderChild { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -#[cfg(test)] -mod tests { - use super::*; - use pretty_assertions::assert_eq; - - #[test] - fn test_output() { - let items = Vec::from(["item1"]); - - let finder = Finder { - program: "fzf".into(), - args: ["-1".into()].into(), - }; - - let selected = finder.spawn().unwrap().find(items).unwrap().stdout; - assert_eq!(selected.as_slice(), "item1\n".as_bytes()) - } - - #[test] - #[ignore] - fn test_selection() { - let items = Vec::from(["item1", "item2", "item3", "item4"]); - - let finder = Finder { - program: "fzf".into(), - args: [].into(), - }; - - let selected = finder.spawn().unwrap().find(items).unwrap().stdout; - assert_eq!(selected.as_slice(), "item1\n".as_bytes()) - } -} diff --git a/src/finder/config.rs b/src/finder/config.rs deleted file mode 100644 index 4c3abda..0000000 --- a/src/finder/config.rs +++ /dev/null @@ -1,84 +0,0 @@ -use figment::{providers::Serialized, value, Figment, Metadata, Profile, Provider}; -use serde::{Deserialize, Serialize}; - -#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] -#[serde(default)] -pub struct Config { - pub program: String, - pub args: Vec<String>, -} - -impl Config { - // Extract the configuration from any `Provider` - pub fn extract<T: Provider>(provider: T) -> figment::error::Result<Config> { - Figment::from(provider).extract() - } - - // Provide a default provider, a `Figment`. - pub fn figment() -> Figment { - Figment::from(Config::default()) - } -} - -impl Default for Config { - fn default() -> Self { - Self { - program: "fzf-tmux".into(), - args: [ - "-p", - "--", - "--multi", - "--print-query", - "-d/", - "--preview-window=right,75%,<80(up,75%,border-bottom)", - "--preview='sel={}; less ${sel:-{q}} 2>/dev/null'", - ] - .map(Into::into) - .to_vec(), - } - } -} - -impl Provider for Config { - fn metadata(&self) -> Metadata { - Metadata::named("Tmuxr finder config") - } - - fn data(&self) -> figment::error::Result<value::Map<Profile, value::Dict>> { - Serialized::defaults(self).data() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use figment::providers::{Format, Serialized, Toml}; - use pretty_assertions::assert_eq; - - #[test] - fn test_extract() { - figment::Jail::expect_with(|jail| { - jail.create_file( - "tmuxr.toml", - r#" - program = "fzf" - args = ["-0", "-1", "--preview='cat'"] - "#, - )?; - - let config: Config = Figment::from(Serialized::defaults(Config::default())) - .merge(Toml::file("tmuxr.toml")) - .extract()?; - - assert_eq!( - config, - Config { - program: "fzf".into(), - args: ["-0", "-1", "--preview='cat'"].map(Into::into).to_vec(), - } - ); - - Ok(()) - }); - } -} diff --git a/src/finder/error.rs b/src/finder/error.rs deleted file mode 100644 index a8009a8..0000000 --- a/src/finder/error.rs +++ /dev/null @@ -1,16 +0,0 @@ -pub type Result<T> = std::result::Result<T, Error>; - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error("Child process has not been spawned")] - NoChild, - - #[error("Failed to get Child's stdin")] - Stdin, - - #[error("Config error: {0}")] - Config(#[from] figment::error::Error), - - #[error("IO error: {0}")] - IO(#[from] std::io::Error), -} @@ -1,13 +1,9 @@ pub use crate::cli::Cli; pub use crate::config::Config; pub use crate::error::{Error, Result}; -pub use crate::finder::Finder; -pub use crate::logging::Logging; -pub use crate::paths::Paths; +pub use crate::projects::{SearchPath, Projects}; mod cli; mod config; mod error; -mod finder; -mod logging; -mod paths; +mod projects; diff --git a/src/logging.rs b/src/logging.rs deleted file mode 100644 index ee48518..0000000 --- a/src/logging.rs +++ /dev/null @@ -1,65 +0,0 @@ -use figment::Provider; -use std::{fs::File, ops::Deref, sync::Arc}; -use tracing::metadata::LevelFilter; -use tracing_subscriber::{prelude::*, Layer}; - -pub(crate) use config::Config; -pub(crate) use error::{Error, Result}; - -pub use level::Level; - -mod config; -mod error; -mod level; - -pub struct Logging(Config); - -impl Logging { - pub fn new() -> Result<Self> { - Self::from_provider(Config::figment()) - } - - /// Extract `Config` from `provider` to construct new `Finder` - pub fn from_provider<T: Provider>(provider: T) -> Result<Self> { - Config::extract(&provider) - .map_err(Into::into) - .map(Into::into) - } - - pub fn init(&self) -> Result<()> { - let stdout_layer = tracing_subscriber::fmt::layer() - .pretty() - .with_filter(LevelFilter::from(self.level)); - - let log_layer = if self.level.is_some() { - let file = File::create(&self.path)?; - tracing_subscriber::fmt::layer() - .with_writer(Arc::new(file)) - .with_filter(LevelFilter::from(self.level)) - .into() - } else { - None - }; - - tracing_subscriber::registry() - .with(stdout_layer) - .with(log_layer) - .init(); - - Ok(()) - } -} - -impl Deref for Logging { - type Target = Config; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl From<Config> for Logging { - fn from(value: Config) -> Self { - Self(value) - } -} diff --git a/src/logging/config.rs b/src/logging/config.rs deleted file mode 100644 index d9f14a6..0000000 --- a/src/logging/config.rs +++ /dev/null @@ -1,87 +0,0 @@ -use super::level; -use figment::{providers::Serialized, value, Figment, Metadata, Profile, Provider}; -use serde::{Deserialize, Serialize}; -use std::path::PathBuf; - -pub(crate) use level::Level; - -#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] -#[serde(default)] -pub struct Config { - #[serde(with = "level")] - pub stdout: Option<Level>, - #[serde(with = "level")] - pub level: Option<Level>, - pub path: PathBuf, -} - -impl Config { - // Extract the configuration from any `Provider` - pub fn extract<T: Provider>(provider: T) -> figment::error::Result<Config> { - Figment::from(provider).extract() - } - - // Provide a default provider, a `Figment`. - pub fn figment() -> Figment { - Figment::from(Config::default()) - } -} - -impl Provider for Config { - fn metadata(&self) -> Metadata { - Metadata::named("Tmuxr path config") - } - - fn data(&self) -> figment::error::Result<value::Map<Profile, value::Dict>> { - Serialized::defaults(self).data() - } -} - -impl Default for Config { - fn default() -> Self { - Self { - stdout: Some(Level::ERROR), - level: None, - path: dirs::cache_dir() - .map(|p| p.join("tmuxr")) - .unwrap_or_default() - .join("tmuxr.log"), - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use figment::providers::{Format, Serialized, Toml}; - use pretty_assertions::assert_eq; - - #[test] - fn test_extract() { - figment::Jail::expect_with(|jail| { - jail.create_file( - "tmuxr.toml", - r#" - stdout = "warn" - level = "info" - path = "/path/to/logfile.log" - "#, - )?; - - let config: Config = Figment::from(Serialized::defaults(Config::default())) - .merge(Toml::file("tmuxr.toml")) - .extract()?; - - assert_eq!( - config, - Config { - stdout: Some(Level::WARN), - level: Some(Level::INFO), - path: "/path/to/logfile.log".into() - } - ); - - Ok(()) - }); - } -} diff --git a/src/logging/error.rs b/src/logging/error.rs deleted file mode 100644 index a38cdda..0000000 --- a/src/logging/error.rs +++ /dev/null @@ -1,10 +0,0 @@ -pub type Result<T> = std::result::Result<T, Error>; - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error(transparent)] - Config(#[from] figment::error::Error), - - #[error(transparent)] - IO(#[from] std::io::Error), -} diff --git a/src/logging/level.rs b/src/logging/level.rs deleted file mode 100644 index 38a0f7f..0000000 --- a/src/logging/level.rs +++ /dev/null @@ -1,21 +0,0 @@ -use serde::{Deserialize, Deserializer, Serialize, Serializer}; -pub use tracing::Level; - -pub fn serialize<S>(value: &Option<Level>, serializer: S) -> Result<S::Ok, S::Error> -where - S: Serializer, -{ - value - .map(|l| l.to_string().to_lowercase()) - .serialize(serializer) -} - -pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Level>, D::Error> -where - D: Deserializer<'de>, -{ - let Some(s) = Option::<String>::deserialize(deserializer)? else { - return Ok(None) -}; - s.parse().map_err(serde::de::Error::custom).map(Some) -} diff --git a/src/main.rs b/src/main.rs index c682def..80126b2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,36 +1,24 @@ use anyhow::{Context, Result}; use clap::Parser; use figment::providers::{Env, Format, Toml}; -use tmuxr::{Cli, Config, Finder, Logging, Paths}; -use tracing::info; +use projectr::{Cli, Config, Projects}; fn main() -> Result<()> { let config = Config::figment() .merge(Cli::parse()) - .merge(Toml::file("tmuxr.toml")) - .merge(Env::prefixed("TMUXR_")) + .merge(Toml::file("projectr.toml")) + .merge(Env::prefixed("PROJECTR_")) .extract() .context("Failed to extract config")?; - Logging::from_provider(&config) - .context("Failed to extract logging config")? - .init() - .context("Failed to initialize logging")?; - run(&config) } #[tracing::instrument] pub fn run(config: &Config) -> Result<()> { - let paths = Paths::from_provider(config).context("Failed to extract paths config")?; - let selected = Finder::from_provider(config) - .context("Failed to extract finder config")? - .spawn() - .context("Failed to spawn finder process")? - .find(paths) - .context("Failed to write paths to finder stdin")?; - - info!("{:?}", selected); + Projects::from_provider(config) + .context("Failed to extract paths config")? + .for_each(|path| println!("{}", path.to_string_lossy())); Ok(()) } diff --git a/src/paths/config.rs b/src/paths/config.rs deleted file mode 100644 index 1e9bc65..0000000 --- a/src/paths/config.rs +++ /dev/null @@ -1,82 +0,0 @@ -use super::PathEntry; -use figment::{providers::Serialized, value, Figment, Metadata, Profile, Provider}; -use serde::{Deserialize, Serialize}; - -#[derive(Debug, PartialEq, Eq, Clone, Default, Serialize, Deserialize)] -pub struct Config { - pub(crate) paths: Vec<PathEntry>, -} - -impl Config { - // Extract the configuration from any `Provider` - pub fn extract<T: Provider>(provider: T) -> figment::error::Result<Config> { - Figment::from(provider).extract() - } - - // Provide a default provider, a `Figment`. - pub fn figment() -> Figment { - Figment::from(Config::default()) - } -} - -impl Provider for Config { - fn metadata(&self) -> Metadata { - Metadata::named("Tmuxr path config") - } - - fn data(&self) -> figment::error::Result<value::Map<Profile, value::Dict>> { - Serialized::defaults(self).data() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use figment::providers::{Format, Serialized, Toml}; - use pretty_assertions::assert_eq; - - #[test] - fn test_extract() { - figment::Jail::expect_with(|jail| { - jail.create_file( - "tmuxr.toml", - r#" - paths = [ - "/path/to/projects", - { path = "/path/to/other_projects", recurse = 1, hidden = true }, - { path = "/path/to/another_project", recurse = 0 }, - ] - "#, - )?; - - let config: Config = Figment::from(Serialized::defaults(Config::default())) - .merge(Toml::file("tmuxr.toml")) - .extract()?; - - assert_eq!( - config, - Config { - paths: Vec::from([ - PathEntry { - path: "/path/to/projects".into(), - hidden: false, - recurse: None, - }, - PathEntry { - path: "/path/to/other_projects".into(), - hidden: true, - recurse: Some(1), - }, - PathEntry { - path: "/path/to/another_project".into(), - hidden: false, - recurse: Some(0), - }, - ]), - } - ); - - Ok(()) - }); - } -} diff --git a/src/paths/error.rs b/src/paths/error.rs deleted file mode 100644 index 4300e8a..0000000 --- a/src/paths/error.rs +++ /dev/null @@ -1,10 +0,0 @@ -pub type Result<T> = std::result::Result<T, Error>; - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error(transparent)] - Config(#[from] figment::error::Error), - - #[error(transparent)] - Ignore(#[from] ignore::Error), -} diff --git a/src/paths.rs b/src/projects.rs index b53eb93..70ea891 100644 --- a/src/paths.rs +++ b/src/projects.rs @@ -1,23 +1,19 @@ +use crate::{Config, Result}; use figment::Provider; use ignore::{Walk, WalkBuilder}; use std::{path::PathBuf, vec::IntoIter}; use tracing::warn; -pub(crate) use config::Config; -pub(crate) use error::{Error, Result}; +pub use entry::SearchPath; -pub use path_entry::PathEntry; +mod entry; -mod config; -mod error; -mod path_entry; - -pub struct Paths { - paths_iter: IntoIter<PathEntry>, +pub struct Projects { + paths_iter: IntoIter<SearchPath>, iter: Option<Walk>, } -impl Paths { +impl Projects { pub fn new() -> Result<Self> { Self::from_provider(Config::figment()) } @@ -30,8 +26,8 @@ impl Paths { } } -impl From<Vec<PathEntry>> for Paths { - fn from(value: Vec<PathEntry>) -> Self { +impl From<Vec<SearchPath>> for Projects { + fn from(value: Vec<SearchPath>) -> Self { Self { paths_iter: value.into_iter(), iter: None, @@ -39,13 +35,13 @@ impl From<Vec<PathEntry>> for Paths { } } -impl From<Config> for Paths { +impl From<Config> for Projects { fn from(value: Config) -> Self { value.paths.into() } } -impl Iterator for Paths { +impl Iterator for Projects { type Item = PathBuf; #[tracing::instrument(skip(self))] @@ -89,18 +85,18 @@ mod tests { let project3 = temp_dir.join("project3"); let project4 = temp_dir.join("subdir/project4"); - let paths = Paths::from(Vec::from([ - PathEntry { + let paths = Projects::from(Vec::from([ + SearchPath { path: project_dir.to_owned(), hidden: false, recurse: Some(1), }, - PathEntry { + SearchPath { path: project3.to_owned(), hidden: false, recurse: Some(0), }, - PathEntry { + SearchPath { path: project4.to_owned(), hidden: false, recurse: Some(0), diff --git a/src/paths/path_entry.rs b/src/projects/entry.rs index b050009..86c23b4 100644 --- a/src/paths/path_entry.rs +++ b/src/projects/entry.rs @@ -2,13 +2,13 @@ use serde::{Deserialize, Deserializer, Serialize}; use std::{convert::Infallible, path::PathBuf, str::FromStr}; #[derive(Debug, PartialEq, Eq, Clone, Default, Serialize)] -pub struct PathEntry { +pub struct SearchPath { pub path: PathBuf, pub hidden: bool, pub recurse: Option<usize>, } -impl From<PathBuf> for PathEntry { +impl From<PathBuf> for SearchPath { fn from(path: PathBuf) -> Self { Self { path, @@ -17,7 +17,7 @@ impl From<PathBuf> for PathEntry { } } -impl FromStr for PathEntry { +impl FromStr for SearchPath { type Err = Infallible; fn from_str(s: &str) -> Result<Self, Self::Err> { @@ -25,7 +25,7 @@ impl FromStr for PathEntry { } } -impl<'de> Deserialize<'de> for PathEntry { +impl<'de> Deserialize<'de> for SearchPath { fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de>, |