diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/cli.rs | 89 | ||||
-rw-r--r-- | src/config.rs | 65 | ||||
-rw-r--r-- | src/error.rs | 4 | ||||
-rw-r--r-- | src/lib.rs | 2 | ||||
-rw-r--r-- | src/logging.rs | 36 | ||||
-rw-r--r-- | src/logging/config.rs | 49 | ||||
-rw-r--r-- | src/logging/level.rs | 22 | ||||
-rw-r--r-- | src/main.rs | 35 |
8 files changed, 172 insertions, 130 deletions
@@ -1,10 +1,9 @@ +use crate::{paths::PathEntry, Config}; use clap::{Args, Parser}; use figment::{providers::Serialized, value, Metadata, Profile, Provider}; use serde::Serialize; use std::path::PathBuf; -use tracing_subscriber::{filter::LevelFilter, Layer, Registry}; - -use crate::{paths::PathEntry, Config}; +use tracing::Level; /// Simple program to manage projects and ssh hosts using tmux #[derive(Debug, Clone, Default, Parser, Serialize)] @@ -36,47 +35,43 @@ pub struct Cli { pub ssh: Option<String>, } -impl Cli { - pub fn as_layer(&self) -> Vec<Box<dyn Layer<Registry> + Send + Sync>> { - let fmt_layer = tracing_subscriber::fmt::layer() - .pretty() - .with_filter(self.verbose.as_filter()) - .boxed(); +impl Provider for Cli { + fn metadata(&self) -> Metadata { + Metadata::named("Tmuxr cli provider") + } - vec![fmt_layer] + fn data(&self) -> figment::error::Result<value::Map<Profile, value::Dict>> { + Serialized::defaults(Self::default()).data() } } impl From<Cli> for Config { fn from(value: Cli) -> Self { + let paths = value + .paths + .iter() + .cloned() + .map(|p| PathEntry { + path: p, + hidden: value.hidden, + recurse: value.max_depth, + }) + .collect(); + + let paths = crate::paths::Config { paths }; + let logging = crate::logging::Config { + stdout: value.verbose.into(), + ..Default::default() + }; + Config { - paths: crate::paths::Config { - paths: value - .paths - .iter() - .cloned() - .map(|p| PathEntry { - path: p, - hidden: value.hidden, - recurse: value.max_depth, - }) - .collect(), - }, + paths, + logging, ..Default::default() } } } -impl Provider for Cli { - fn metadata(&self) -> Metadata { - Metadata::named("Tmuxr cli provider") - } - - fn data(&self) -> figment::error::Result<value::Map<Profile, value::Dict>> { - Serialized::defaults(Self::default()).data() - } -} - #[derive(Debug, Default, Clone, Args)] pub struct Verbosity { /// Print additional information per occurrence @@ -88,29 +83,15 @@ pub struct Verbosity { pub quiet: bool, } -impl Verbosity { - pub fn as_filter(&self) -> LevelFilter { - self.into() - } -} - -impl From<&Verbosity> for LevelFilter { - fn from(value: &Verbosity) -> Self { +impl From<Verbosity> for Option<Level> { + fn from(value: Verbosity) -> Self { match value.verbose + 1 - u8::from(value.quiet) { - 0 => LevelFilter::OFF, - 1 => LevelFilter::ERROR, - 2 => LevelFilter::WARN, - 3 => LevelFilter::INFO, - 4 => LevelFilter::DEBUG, - _ => LevelFilter::TRACE, + 0 => None, + 1 => Some(Level::ERROR), + 2 => Some(Level::WARN), + 3 => Some(Level::INFO), + 4 => Some(Level::DEBUG), + _ => Some(Level::TRACE), } } } - -#[cfg(test)] -mod tests { - #[test] - fn test_cli_parse() { - assert_eq!(1, 1); - } -} diff --git a/src/config.rs b/src/config.rs index d31eca8..afbb401 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,51 +1,20 @@ -use crate::{Error, Result}; +use crate::{finder, logging, paths, Error, Result}; use figment::{providers::Serialized, value, Figment, Metadata, Profile, Provider}; use serde::{Deserialize, Serialize}; -use std::{fs::File, path::PathBuf, sync::Arc}; -use tracing_subscriber::{Layer, Registry}; -#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, Default, PartialEq, Eq, Serialize, Deserialize)] #[serde(default)] pub struct Config { #[serde(flatten)] - pub paths: crate::paths::Config, - pub finder: crate::finder::Config, - pub log_enabled: bool, - pub log_file: PathBuf, + pub paths: paths::Config, + pub finder: finder::Config, + pub logging: logging::Config, } impl Config { pub fn extract<T: Provider>(provider: T) -> figment::error::Result<Config> { Figment::from(provider).extract() } - - pub fn as_layer(&self) -> Result<Vec<Box<dyn Layer<Registry> + Send + Sync>>> { - let mut layers = Vec::new(); - - if self.log_enabled { - let file = File::create(&self.log_file)?; - 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 { - paths: Default::default(), - finder: Default::default(), - log_enabled: Default::default(), - log_file: dirs::cache_dir() - .map(|p| p.join("tmuxr")) - .unwrap_or_default() - .join("tmuxr.log"), - } - } } impl Provider for Config { @@ -69,7 +38,6 @@ impl TryFrom<Figment> for Config { #[cfg(test)] mod tests { use super::*; - use crate::paths::PathEntry; use figment::providers::{Format, Serialized, Toml}; #[test] @@ -78,8 +46,6 @@ mod tests { jail.create_file( "tmuxr.toml", r#" - log_enabled = true - log_file = "/path/to/log_file" paths = [ "/path/to/projects", { path = "/path/to/other_projects", recurse = 1, hidden = true }, @@ -89,6 +55,10 @@ mod tests { [finder] program = "fzf" args = ["-0", "-1", "--preview='cat'"] + + [logging] + level = "warn" + path = "/path/to/log_file" "#, )?; @@ -99,31 +69,34 @@ mod tests { assert_eq!( config, Config { - paths: crate::paths::Config { + paths: paths::Config { paths: Vec::from([ - PathEntry { + paths::PathEntry { path: "/path/to/projects".into(), hidden: false, recurse: None, }, - PathEntry { + paths::PathEntry { path: "/path/to/other_projects".into(), hidden: true, recurse: Some(1), }, - PathEntry { + paths::PathEntry { path: "/path/to/another_project".into(), hidden: false, recurse: Some(0), }, ]), }, - finder: crate::finder::Config { + finder: finder::Config { program: "fzf".into(), args: vec!["-0".into(), "-1".into(), "--preview='cat'".into()], }, - log_enabled: true, - log_file: "/path/to/log_file".into() + logging: logging::Config { + stdout: None, + level: Some(logging::Level::WARN), + path: "/path/to/log_file".into() + }, } ); diff --git a/src/error.rs b/src/error.rs index 509dba2..a46285b 100644 --- a/src/error.rs +++ b/src/error.rs @@ -11,8 +11,8 @@ pub enum Error { #[error("Config error: {0}")] Config(#[from] figment::error::Error), - #[error("Directories error: {0}")] - Directories(#[from] crate::paths::Error), + #[error("Paths error: {0}")] + Paths(#[from] crate::paths::Error), #[error("Finder error: {0}")] Finder(#[from] crate::finder::Error), @@ -4,6 +4,8 @@ pub use crate::error::{Error, Result}; pub use crate::finder::Finder; pub use crate::paths::Paths; +pub mod logging; + mod cli; mod config; mod error; diff --git a/src/logging.rs b/src/logging.rs new file mode 100644 index 0000000..c97948f --- /dev/null +++ b/src/logging.rs @@ -0,0 +1,36 @@ +use crate::Result; +use figment::Provider; +use std::{fs::File, sync::Arc}; +use tracing::metadata::LevelFilter; +use tracing_subscriber::{prelude::*, Layer}; + +pub use config::Config; +pub use level::Level; + +mod config; +mod level; + +pub fn init_from_provider<T: Provider>(provider: T) -> Result<()> { + let config = Config::extract(&provider)?; + + let stdout_layer = tracing_subscriber::fmt::layer() + .pretty() + .with_filter(LevelFilter::from(config.level)); + + let log_layer = if config.level.is_some() { + let file = File::create(&config.path)?; + tracing_subscriber::fmt::layer() + .with_writer(Arc::new(file)) + .with_filter(LevelFilter::from(config.level)) + .into() + } else { + None + }; + + tracing_subscriber::registry() + .with(stdout_layer) + .with(log_layer) + .init(); + + Ok(()) +} diff --git a/src/logging/config.rs b/src/logging/config.rs new file mode 100644 index 0000000..8626a36 --- /dev/null +++ b/src/logging/config.rs @@ -0,0 +1,49 @@ +use super::level::{self, Level}; +use figment::{providers::Serialized, value, Figment, Metadata, Profile, Provider}; +use serde::{Deserialize, Serialize}; +use std::path::PathBuf; + +#[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::default()).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"), + } + } +} diff --git a/src/logging/level.rs b/src/logging/level.rs new file mode 100644 index 0000000..6134c24 --- /dev/null +++ b/src/logging/level.rs @@ -0,0 +1,22 @@ +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 8ec7006..c85bf2f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,24 +1,24 @@ use clap::Parser; use figment::{ - providers::{Env, Format, Serialized, Toml}, + providers::{Env, Format, Toml}, Figment, }; -use std::{error::Error, fs::File, sync::Arc}; -use tmuxr::{Cli, Config, Finder, Paths, Result}; +use std::error::Error; +use tmuxr::{logging, Cli, Config, Finder, Paths, Result}; use tracing::info; -use tracing_subscriber::prelude::*; fn main() -> Result<()> { let cli = Cli::parse(); - let config = Figment::from(Serialized::defaults(Config::default())) - .merge(Serialized::defaults(&cli)) + let config = Figment::from(Config::default()) + .merge(cli) .merge(Toml::file("tmuxr.toml")) .merge(Env::prefixed("TMUXR_")) .extract() .map_err(eprintln)?; - init_subscriber(&cli, &config).map_err(eprintln)?; + logging::init_from_provider(&config).map_err(eprintln)?; + run(&config).map_err(eprintln) } @@ -27,27 +27,6 @@ fn eprintln<E: Error>(err: E) -> E { err } -fn init_subscriber(cli: &Cli, config: &Config) -> Result<()> { - let stdout_layer = tracing_subscriber::fmt::layer() - .pretty() - .with_filter(cli.verbose.as_filter()); - - let log_layer = if config.log_enabled { - let file = File::create(&config.log_file)?; - let log_layer = tracing_subscriber::fmt::layer().with_writer(Arc::new(file)); - Some(log_layer) - } else { - None - }; - - tracing_subscriber::registry() - .with(stdout_layer) - .with(log_layer) - .init(); - - Ok(()) -} - #[tracing::instrument()] pub fn run(config: &Config) -> Result<()> { let paths = Paths::from_provider(config)?; |