summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/cli.rs89
-rw-r--r--src/config.rs65
-rw-r--r--src/error.rs4
-rw-r--r--src/lib.rs2
-rw-r--r--src/logging.rs36
-rw-r--r--src/logging/config.rs49
-rw-r--r--src/logging/level.rs22
-rw-r--r--src/main.rs35
8 files changed, 172 insertions, 130 deletions
diff --git a/src/cli.rs b/src/cli.rs
index 657d05d..c9bd6b6 100644
--- a/src/cli.rs
+++ b/src/cli.rs
@@ -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),
diff --git a/src/lib.rs b/src/lib.rs
index 66f5acb..f7c3c4f 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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)?;