From 1334d9f1d08a27ea7fa4cd4228ac1fdf44bdd552 Mon Sep 17 00:00:00 2001 From: Toby Vincent Date: Tue, 1 Nov 2022 17:48:54 -0500 Subject: feat: impl ignore crate to walk directories --- src/config.rs | 247 ++++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 181 insertions(+), 66 deletions(-) (limited to 'src/config.rs') diff --git a/src/config.rs b/src/config.rs index 42614ec..2c61b88 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,64 +1,99 @@ use crate::{Error, Result}; -use clap::{Args, Parser}; use figment::{ providers::{Env, Format, Serialized, Toml}, Figment, }; +use ignore::WalkBuilder; use serde::{Deserialize, Serialize}; -use std::path::PathBuf; +use std::{fmt::Display, fs::File, path::PathBuf, sync::Arc}; +use tracing::{info, warn}; +use tracing_subscriber::{Layer, Registry}; -/// Simple program to manage projects and ssh hosts using tmux -#[derive(Debug, Parser, Serialize, Deserialize)] -#[command(author, version, about)] +#[derive(Debug, PartialEq, Eq, Default, Serialize, Deserialize)] pub struct Config { - /// Path to search for directories - pub(crate) path: Vec, - - /// Stand alone directory. Can be specified multiple times - #[arg(short, long)] - pub(crate) project: Vec, - - /// Allows traversal into hidden directories when searching - #[arg(long)] - pub(crate) hidden: bool, - - #[arg(skip)] + pub(crate) paths: Paths, pub(crate) finder: Finder, - - #[arg(skip)] - pub(crate) enable_logging: bool, - - #[arg(skip)] - pub(crate) log_file: PathBuf, + pub(crate) logging: Logging, } impl Config { - pub fn extract(&self) -> Result { + pub fn extract(self) -> Result { Figment::from(Serialized::defaults(self)) .merge(Toml::file("tmuxr.toml")) .merge(Env::prefixed("TMUXR_")) .extract() .map_err(Error::from) } + + pub fn as_layer(&self) -> Result + Send + Sync>>> { + let mut layers = Vec::new(); + + if self.logging.enabled { + let file = File::create(&self.logging.path)?; + let log_layer = tracing_subscriber::fmt::layer() + .with_writer(Arc::new(file)) + .boxed(); + layers.push(log_layer); + }; + + Ok(layers) + } } -impl Default for Config { - fn default() -> Self { - Self { - path: Default::default(), - project: Default::default(), - hidden: Default::default(), - finder: Default::default(), - enable_logging: Default::default(), - log_file: dirs::cache_dir() - .map(|p| p.join("tmuxr")) - .unwrap_or_default() - .join("tmuxr.log"), - } +#[derive(Debug, PartialEq, Eq, Default, Serialize, Deserialize)] +pub(crate) struct Paths { + pub(crate) search: Vec, + pub(crate) add: Vec, + pub(crate) hidden: bool, +} + +impl Display for Paths { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + Vec::::from(self) + .into_iter() + .filter_map(|p: PathBuf| { + p.to_str().map(|s| s.to_owned()).or_else(|| { + warn!("Failed to convert path to str"); + None + }) + }) + .map(|s| writeln!(f, "{}", s)) + .collect::() + } +} + +impl From<&Paths> for Vec { + fn from(value: &Paths) -> Self { + let mut dirs = value.search.to_owned().into_iter(); + let first = dirs.nth(0).unwrap_or_default(); + + let mut path_bufs = dirs + .fold( + WalkBuilder::new(first) + .standard_filters(true) + .max_depth(Some(1)), + |walk, dir| walk.add(dir), + ) + .build() + .filter_map(|res| { + res.map_err(|err| { + warn!("Error while walking directory: {:?}", err); + err + }) + .ok() + }) + .map(|entry| entry.into_path()) + .collect::>(); + + info!("{:?}", value.add); + info!("{:?}", path_bufs); + + path_bufs.extend(value.add.to_owned()); + path_bufs } } -#[derive(Debug, Args, Serialize, Deserialize)] +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] pub(crate) struct Finder { pub(crate) program: String, pub(crate) args: Vec, @@ -69,46 +104,126 @@ impl Default for Finder { Self { program: "fzf-tmux".into(), args: vec![ - "--".into(), - "--multi".into(), - "--print-query".into(), - "-d/".into(), - "--preview-window='right,75%,<80(up,75%,border-bottom)'".into(), - "--preview='sel={}; less ${sel:-{q}} 2>/dev/null'".into(), - ], + "--", + "--multi", + "--print-query", + "-d/", + "--preview-window='right,75%,<80(up,75%,border-bottom)'", + "--preview='sel={}; less ${sel:-{q}} 2>/dev/null'", + ] + .into_iter() + .map(Into::into) + .collect(), } } } -#[derive(Debug, Args, Serialize, Deserialize)] -pub(crate) struct Directory { +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] +pub(crate) struct Logging { + pub(crate) enabled: bool, pub(crate) path: PathBuf, - pub(crate) root: bool, - pub(crate) hidden: bool, -} - -#[derive(Debug, Default)] -pub(crate) struct Directories { - search_dirs: Vec, - root_dirs: Vec, } -impl From> for Directories { - fn from(value: Vec) -> Self { - value.iter().fold(Directories::default(), |mut acc, d| { - match d.root { - true => acc.root_dirs.push(d.path.to_owned()), - false => acc.search_dirs.push(d.path.to_owned()), - }; - acc - }) +impl Default for Logging { + fn default() -> Self { + Self { + enabled: Default::default(), + 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}; + + #[test] + fn defaults() { + figment::Jail::expect_with(|jail| { + jail.create_file( + "tmuxr.toml", + r#" + [paths] + search = [] + add = [] + hidden = false + + [finder] + program = "fzf-tmux" + args = [ + "--", + "--multi", + "--print-query", + "-d/", + "--preview-window='right,75%,<80(up,75%,border-bottom)'", + "--preview='sel={}; less ${sel:-{q}} 2>/dev/null'", + ] + + [logging] + enabled = true + path = "/tmp/tmuxr/test/tmuxr.log" + "#, + )?; + + let config: Config = Figment::from(Serialized::defaults(Config::default())) + .merge(Toml::file("tmuxr.toml")) + .extract()?; + + assert_eq!(config, Config::default()); + + Ok(()) + }); + } + #[test] - fn test_start() { - assert_eq!(1, 1); + fn custom() { + figment::Jail::expect_with(|jail| { + jail.create_file( + "tmuxr.toml", + r#" + [paths] + search = [ "/tmp/tmuxr/test/projects" ] + add = [ "/tmp/tmuxr/test/other_projects" ] + hidden = true + + [finder] + program = "fzf" + args = ["-0", "-1", "--preview='cat'"] + + [logging] + enabled = true + log_file = "/tmp/tmuxr/test/tmuxr.log" + "#, + )?; + + let config: Config = Figment::from(Serialized::defaults(Config::default())) + .merge(Toml::file("tmuxr.toml")) + .extract()?; + + assert_eq!( + config, + Config { + paths: Paths { + search: vec!["/tmp/tmuxr/test/projects".into()], + add: vec!["/tmp/tmuxr/test/extra_project".into()], + hidden: true, + }, + finder: Finder { + program: "fzf".into(), + args: vec!["-0".into(), "-1".into(), "--preview='cat'".into()], + }, + logging: Logging { + enabled: true, + path: "/tmp/tmuxr/test/tmuxr.log".into() + }, + } + ); + + Ok(()) + }); } } -- cgit v1.2.3-70-g09d2