diff --git a/Cargo.lock b/Cargo.lock index 36ab37a..5746e91 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -374,6 +374,12 @@ dependencies = [ "winapi-build", ] +[[package]] +name = "lazy_format" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f389fd31a70d3b0f1d9d80e8f7a9366127fe5fb819f4d4dd21122a167352da8f" + [[package]] name = "lazy_static" version = "1.4.0" @@ -952,6 +958,7 @@ dependencies = [ "chrono", "color-eyre", "futures", + "lazy_format", "lazy_static", "log", "pretty_env_logger", diff --git a/Cargo.toml b/Cargo.toml index 9f35634..b2ca13c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,7 @@ log = "0.4.11" pretty_env_logger = "0.4.0" termprogress = "0.3.4" uuid = {version = "0.8.1", features=["v4"]} +lazy_format = "1.8.3" [build-dependencies] rustc_version = "0.2" diff --git a/src/delete.rs b/src/delete.rs index efce546..5af5871 100644 --- a/src/delete.rs +++ b/src/delete.rs @@ -31,7 +31,7 @@ async fn process_single(state: Arc, path: impl AsRef) -> eyr debug_assert!(path.is_file(), "process_single() expected a file, but {:?} is not one.", path); let _g = state.lock().await; - debug!("{:?} Processing", path); + debug!(state.logger_output() => "{:?} Processing", path); Ok(()) } diff --git a/src/main.rs b/src/main.rs index 341ad25..21bcd52 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,7 +3,7 @@ #![allow(dead_code)] -#[macro_use] extern crate log; +//#[macro_use] extern crate log; use cfg_if::cfg_if; use lazy_static::lazy_static; @@ -37,7 +37,7 @@ mod database; mod args; mod config; -mod progress; +#[macro_use] mod progress; mod state; mod delete; mod restore; @@ -79,15 +79,26 @@ async fn process_mock(state: Arc, file: String) async fn process(state: Arc, file: String) -> eyre::Result<()> { let path = std::path::Path::new(&file); - if !path.exists() { - error!("{:?} does not exist, skipping", path); + let mut progress = state.progress().clone(); + let task_id_fut = progress.send_command(progress::CommandKind::AddTask(file.clone())).await?; + let res = if !path.exists() { + error!(yield state.logger_output() => "{:?} does not exist, skipping", path); + Ok(()) } else { - info!("{:?} Processing", path); + info!(state.logger_output() => "{:?} Processing", path); delete::process(state, path).await .wrap_err(eyre!("Processing failed")) - .with_section(move || file.header("Root path was"))?; - } - Ok(()) + .with_section(move || file.header("Root path was")) + }; + progress.send_command_and_wait(progress::CommandKind:: + RemoveTask(task_id_fut.await? + .map(|x| x.downcast() + .ok() + .map(|x| *x)) + .flatten() + .unwrap())).await?; + + res } async fn validate_config(config: config::Config) -> eyre::Result @@ -119,14 +130,21 @@ async fn begin() -> eyre::Result }, })) }; - - //let task_id = progress.send_command_and_downcast(progress::CommandKind::AddTask("Task name".to_owned())).await?.unwrap(); - //progress.send_command_and_wait(progress::CommandKind::AddTask("Task 2 name".to_owned())).await?; + let state = { + use progress::logging; + let mut logger = logging::Logger::new(progress, + logging::Level::Info); //TODO: Get level from env var + + //let task_id = progress.send_command_and_downcast(progress::CommandKind::AddTask("Task name".to_owned())).await?.unwrap(); + //progress.send_command_and_wait(progress::CommandKind::AddTask("Task 2 name".to_owned())).await?; + + let state = Arc::new(state::State::new(validate_config(config::Config::default()).await + .wrap_err(eyre!("Failed to validate config"))?, + logger.clone())); + info!(yield logger => "Validated config OK"); + state + }; - let state = Arc::new(state::State::new(validate_config(config::Config::default()).await - .wrap_err(eyre!("Failed to validate config"))?, - progress)); - info!("Validated config OK"); if args::process(|file| { let state = Arc::clone(&state); use futures::future::TryFutureExt; diff --git a/src/progress/logging.rs b/src/progress/logging.rs new file mode 100644 index 0000000..9e5843e --- /dev/null +++ b/src/progress/logging.rs @@ -0,0 +1,239 @@ +//! Logging +use super::*; + +/// The logging level +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum Level +{ + Trace, + Debug, + Info, + Warning, + Error, + Fatal, +} + +impl fmt::Display for Level +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + write!(f, "{}", match self { + Level::Trace => "TRACE", + Level::Debug => "DEBUG", + Level::Info => "INFO", + Level::Warning => "WARNING", + Level::Error => "ERROR", + Level::Fatal => "FATAL", + }) + } +} + + +impl Level +{ + fn is_err(&self) -> bool + { + match self { + Self::Error | + Self::Fatal | + Self::Warning => true, + _ => false, + } + } + + fn should_print(&self, other: &Self) -> bool + { + other >= self + } +} + +impl Default for Level +{ + #[inline] + fn default() -> Self + { + Self::Info + } +} + +/// Contains logging state +#[derive(Debug)] +pub struct Logger +{ + level: Level, + progress: Handle, +} + +impl Clone for Logger +{ + fn clone(&self) -> Self + { + Self { + level: self.level, + progress: self.progress.clone(), + } + } +} + +impl Logger +{ + /// Create a new logging state + pub fn new(progress: Handle, level: Level) -> Self + { + Self{progress, level} + } + + /// Get a reference of the progress handle + pub fn progress(&self) -> &Handle + { + &self.progress + } + /// Get a mutable reference of the progress handle + pub fn progress_mut(&mut self) -> &mut Handle + { + &mut self.progress + } + + /// Get the logging level + pub fn level(&self) -> &Level + { + &self.level + } + + /// Set the logging level + pub fn level_mut(&mut self) -> &mut Level + { + &mut self.level + } + + fn gen_string(&self, level: &Level, disp: impl fmt::Display) -> String + { + format!("[ {} ]: \t{}", level, disp) + } + + pub async fn print_display(&mut self, level: Level, disp: impl fmt::Display) -> Result>, Error> + { + if self.level.should_print(&level) { + let string = self.gen_string(&level, disp); + + self.progress.send_command(if level.is_err() {CommandKind::LineErr(string)} else {CommandKind::Line(string)}).await + .map_err(Error::Progress) + .map(|x| x + .map_err(Error::Progress)) + } else { + Err(Error::Level) + } + } + pub async fn print_display_and_wait(&mut self, level: Level, disp: impl fmt::Display) -> Result + { + Ok(self.print_display(level, disp).await?.await?) + } + pub async fn print_display_and_detach(&mut self, level: Level, disp: impl fmt::Display) -> Result<(), Error> + { + let _ = self.print_display(level, disp).await?; + Ok(()) + } +} + +#[non_exhaustive] +#[derive(Debug)] +pub enum Error +{ + Progress(WorkerCommError), + Level, +} + +impl error::Error for Error +{ + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + Some(match &self { + Self::Progress(p) => p, + _ => return None, + }) + } +} + +impl fmt::Display for Error +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + match self { + Self::Progress(_) => write!(f, "sending to progress worker failed"), + Self::Level => write!(f, "invalid level for write"), + } + } +} + + +impl From for Error +{ + #[inline] fn from(from: WorkerCommError) -> Self + { + Self::Progress(from) + } +} + +#[macro_export] macro_rules! trace { + (yield $logger:expr => $msg:literal $($rest:tt)*) => { + { + let _ = $logger.print_display_and_wait($crate::progress::logging::Level::Trace, ::lazy_format::lazy_format!($msg $($rest)*)).await; + } + }; + ($logger:expr => $msg:literal $($rest:tt)*) => { + { + let _ = $logger.print_display_and_detach($crate::progress::logging::Level::Trace, ::lazy_format::lazy_format!($msg $($rest)*)).await; + } + }; +} + +#[macro_export] macro_rules! debug { + (yield $logger:expr => $msg:literal $($rest:tt)*) => { + { + let _ = $logger.print_display_and_wait($crate::progress::logging::Level::Debug, ::lazy_format::lazy_format!($msg $($rest)*)).await; + } + }; + ($logger:expr => $msg:literal $($rest:tt)*) => { + { + let _ = $logger.print_display_and_detach($crate::progress::logging::Level::Debug, ::lazy_format::lazy_format!($msg $($rest)*)).await; + } + }; +} + +#[macro_export] macro_rules! info { + (yield $logger:expr => $msg:literal $($rest:tt)*) => { + { + let _ = $logger.print_display_and_wait($crate::progress::logging::Level::Info, ::lazy_format::lazy_format!($msg $($rest)*)).await; + } + }; + ($logger:expr => $msg:literal $($rest:tt)*) => { + { + let _ = $logger.print_display_and_detach($crate::progress::logging::Level::Info, ::lazy_format::lazy_format!($msg $($rest)*)).await; + } + }; +} + +#[macro_export] macro_rules! warn { + (yield $logger:expr => $msg:literal $($rest:tt)*) => { + { + let _ = $logger.print_display_and_wait($crate::progress::logging::Level::Warn, ::lazy_format::lazy_format!($msg $($rest)*)).await; + } + }; + ($logger:expr => $msg:literal $($rest:tt)*) => { + { + let _ = $logger.print_display_and_detach($crate::progress::logging::Level::Warn, ::lazy_format::lazy_format!($msg $($rest)*)).await; + } + }; +} + +#[macro_export] macro_rules! error { + (yield $logger:expr => $msg:literal $($rest:tt)*) => { + { + let _ = $logger.print_display_and_wait($crate::progress::logging::Level::Error, ::lazy_format::lazy_format!($msg $($rest)*)).await; + } + }; + ($logger:expr => $msg:literal $($rest:tt)*) => { + { + let _ = $logger.print_display_and_detach($crate::progress::logging::Level::Error, ::lazy_format::lazy_format!($msg $($rest)*)).await; + } + }; +} diff --git a/src/progress/mod.rs b/src/progress/mod.rs index 61c4700..a49019f 100644 --- a/src/progress/mod.rs +++ b/src/progress/mod.rs @@ -26,6 +26,8 @@ use std::{ error, }; +#[macro_use] pub mod logging; + mod tasklist; pub use tasklist::{ TaskId, diff --git a/src/state.rs b/src/state.rs index 5e58105..b5d4eda 100644 --- a/src/state.rs +++ b/src/state.rs @@ -13,6 +13,8 @@ use futures::{ future::OptionFuture, }; +use progress::logging::Logger; + /// Contains state for a set of operations #[derive(Debug)] pub struct State @@ -21,7 +23,7 @@ pub struct State mtx: Option, stop: RwLock<()>, - progress: progress::Handle, + logger: Logger, } /// Guard for operations inside state @@ -30,14 +32,32 @@ pub struct Permit<'a>(RwLockReadGuard<'a, ()>, Option>); impl State { + /// Get the logger object for this state + pub fn logger(&self) -> &Logger + { + &self.logger + } + + /// Clone the logger into a new mutable one + pub fn logger_output(&self) -> Logger + { + self.logger.clone() //TODO: Eeeehhhhh this is really inefficient + } + + /// A reference to the progress counter + pub fn progress(&self) -> &progress::Handle + { + self.logger.progress() + } + /// Create a new state - pub fn new(cfg: config::Config, progress: progress::Handle) -> Self + pub fn new(cfg: config::Config, logger: progress::logging::Logger) -> Self { Self { mtx: cfg.limit.map(|x| Semaphore::new(x.into())), cfg, stop: RwLock::new(()), - progress, + logger, } }