From 047a6512da5cf0c8e18e3f744721c05544e8ee63 Mon Sep 17 00:00:00 2001 From: Leon Wilzer Date: Tue, 13 Feb 2024 00:56:29 +0100 Subject: [PATCH 01/27] removed newline --- Cargo.toml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 8d023fd..4e7f53f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,5 @@ +workspace = { members = ["config", "logging"] } + [package] name = "wanessa" version = "0.1.0" @@ -17,4 +19,4 @@ nursery = "deny" # wip lints: https://rust-lang.github.io/rust-clippy/master/ind unwrap_used = "deny" # https://rust-lang.github.io/rust-clippy/master/index.html#/unwrap_used missing_const_for_fn = "warn" # https://rust-lang.github.io/rust-clippy/master/index.html#/missing_const_for_fn missing_assert_message = "deny" # https://rust-lang.github.io/rust-clippy/master/index.html#/missing_assert_message -missing_errors_doc = "deny" # https://rust-lang.github.io/rust-clippy/master/index.html#/missing_assert_message \ No newline at end of file +missing_errors_doc = "deny" # https://rust-lang.github.io/rust-clippy/master/index.html#/missing_assert_message -- 2.45.2 From 606312a0eb1888a7a8f9b45569c2eec747c5be6b Mon Sep 17 00:00:00 2001 From: Leon Wilzer Date: Tue, 13 Feb 2024 00:57:20 +0100 Subject: [PATCH 02/27] preliminary config module --- config/Cargo.toml | 9 +++ config/src/lib.rs | 148 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 157 insertions(+) create mode 100644 config/Cargo.toml create mode 100644 config/src/lib.rs diff --git a/config/Cargo.toml b/config/Cargo.toml new file mode 100644 index 0000000..e588f30 --- /dev/null +++ b/config/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "config" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +getset = "0.1.2" diff --git a/config/src/lib.rs b/config/src/lib.rs new file mode 100644 index 0000000..7248f66 --- /dev/null +++ b/config/src/lib.rs @@ -0,0 +1,148 @@ +/*! +* A singleton, thread-safe struct used for accessing +*/ + +use std::path::PathBuf; +use std::sync::RwLock; + +use std::cmp::Ordering; + +use crate::LogVerbosity::Warning; + +use getset::Getters; +use getset::Setters; + +/** +* Represents a valid `WANessa` configuration. Intended as a read-only singleton. +* See [`DEFAULTS`] +*/ +#[derive(Clone,PartialEq, Eq)] +#[derive(Getters, Setters)] +#[getset(get = "pub")] +#[allow(clippy::struct_excessive_bools)] // False positive, since it is a config struct. +pub struct Config { + /// See [`LogVerbosity`].
+ /// Default: [`Warning`] + pub log_verbosity: LogVerbosity, + /// Logs UTC time and date of message, if true.
+ /// Default: `false` + pub log_time: bool, + /// Time and date format.
+ /// Defaults to `%F/%T:%f`.
+ /// See [chrono](https://docs.rs/chrono/latest/chrono/format/strftime/index.html).
+ /// Default example : `2001-12-31/23:59:33:26490000` + #[getset(skip)] + pub log_time_format: Option, + /// Logs location in code, where the message was logged, if true.
+ /// Default: `false` + pub log_location: bool, + /// If `Some(path)` tries to also write the log to `path` in addition to stderr/stderr.
+ /// Default: [None] + pub log_path: Option, + /// Logs to standard out, if true.
+ /// Default: `true` + pub log_stdout: bool, + /// Logs to standard err, if true.
+ /// Default: `true` + pub log_stderr: bool, + /// Database connection address. + /// Is an option to allow constructing a default config during compile time.
+ /// Default: `localhost`. + #[getset(skip)] + pub db_addr: Option, + /// Database connection port.
+ /// Default: `6969` (nice). + pub db_port: u16, +} + +impl Config { + /// Getter for [`Self::db_addr`]. + pub fn db_addr(&self) -> &str { + self.db_addr.as_ref().map_or("localhost", |addr| addr) + } + + /// Getter for the [`Self::log_time_format`]. + pub fn log_time_format(&self) -> &str + { + self.log_time_format.as_ref().map_or("%F-%T:%f", |fmt| fmt) + } +} + +/// See [`DEFAULTS`]. +impl Default for Config { + /// See [`DEFAULTS`]. + fn default() -> Self { + DEFAULTS + } +} + +/// Default configuration. +/// ```rust +/// # use config::Config; +/// # use config::LogVerbosity::Warning; +/// let DEFAULTS = Config +/// { +/// log_verbosity: Warning, +/// log_time: false, +/// log_time_format: None, +/// log_location: false, +/// log_stdout: true, +/// log_stderr: true, +/// log_path: None, +/// db_addr: None, +/// db_port: 6969, +/// }; +/// # assert!(DEFAULTS == config::DEFAULTS) +/// ``` +pub const DEFAULTS: Config = Config { + log_verbosity: Warning, + log_time: false, + log_time_format: None, + log_location: false, + log_stdout: true, + log_stderr: true, + log_path: None, + db_addr: None, + db_port: 6969, +}; + +/// Configuration singleton. +pub static CONFIG: RwLock = RwLock::new(DEFAULTS); + +/** +* Each level includes the previous ones. +*/ +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum LogVerbosity +{ + /// Critical Errors, may lead to crashes or deactivating certain features. + Error, + /// Very minor and recovered errors, such as invalid configs. + Warning, + /// Very verbose and detailed. Basically gives a step-by-step instruction on what is currently done. + Information, + /// Very technical and even more verbose. + /// May contain secrets and private information. + /// **Do not use in production environments!** + Debugging, +} + +impl PartialOrd for LogVerbosity +{ + /// Some operator overloading of comparison symbols (==, <,>=, etc.) as syntactic sugar. + /// See [`PartialOrd`]. + fn partial_cmp(&self, other: &Self) -> Option + { + (*self as usize).partial_cmp(&(*other as usize)) + } +} + +impl Ord for LogVerbosity +{ + /// Some operator overloading of comparison symbols (==, <,>=, etc.) as syntactic sugar. + /// See [`Ord`]. + fn cmp(&self, other: &Self) -> Ordering + { + (*self as usize).cmp(&(*other as usize)) + } +} -- 2.45.2 From d330a48c55c5704709ad8466af74537e712cb7c1 Mon Sep 17 00:00:00 2001 From: Leon Wilzer Date: Tue, 13 Feb 2024 00:57:41 +0100 Subject: [PATCH 03/27] preliminary logging module --- logging/Cargo.toml | 10 +++ logging/src/lib.rs | 149 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 159 insertions(+) create mode 100644 logging/Cargo.toml create mode 100644 logging/src/lib.rs diff --git a/logging/Cargo.toml b/logging/Cargo.toml new file mode 100644 index 0000000..dbe95c9 --- /dev/null +++ b/logging/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "logging" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +chrono = { version = "0.4.34", default-features = false, features = ["now"] } +config = { path = "../config"} diff --git a/logging/src/lib.rs b/logging/src/lib.rs new file mode 100644 index 0000000..0d429ec --- /dev/null +++ b/logging/src/lib.rs @@ -0,0 +1,149 @@ +/*! +* This module handles logging messages asynchronously and is thread-safe. +* It should mostly be called statically. +*/ + +use std::fmt::Display; +use std::fs::OpenOptions; +use std::io::Write; + +use config::LogVerbosity; + +use chrono::Utc; + +use LogVerbosity::Warning; +use LogVerbosity::Information; + +use LogMessageType::GenericErr; +use LogMessageType::GenericWarn; +use LogMessageType::GenericInfo; +use LogMessageType::GenericDebug; + +/** +* Logs the given message. +* +* # Panics +* Panics if readlock on [`config::CONFIG`] could not be acquired +* or if another error occurs, such as a full disk. +*/ +// TODO: Create macro to make last three parameters optional. +pub fn log_message(msg: &LogMessage, file: &str, line: &str, column: &str) +{ + let conf = config::CONFIG.read().expect(&format!("Failed aqcuire read lock on config\nTried to log message: {msg}")); + + if conf.log_verbosity() < &msg.1 { return; } + + let mut log_line = String::new(); + + // add time substring + if *conf.log_time() + { + log_line += &format!("{} ", Utc::now().format(conf.log_time_format())); + } + + // add code location substring + if *conf.log_location() + { + log_line += &format!("{file}:{line},{column} "); + } + + log_line += &msg.to_string(); + + // May panic if file cannot be opened or written to. + conf.log_path().as_ref().map_or_else( + || {}, |path| + { + let mut file = OpenOptions::new() + .write(true) + .append(true) + .open(path) + .expect( + &format!("Could not open log file: {path:#?}") + ); + writeln!(file, "{log_line}").expect(&format!("Could not write log to file: {path:#?}")); + } + ); + + if msg.1 <= Warning && *conf.log_stderr() + { + // May panic if writing to stderr fails. + eprintln!("{log_line}"); + } + else if msg.1 >= Information && *conf.log_stdout() + { + // May panic if writing to stdout fails. + println!("{log_line}"); + } + + drop(conf); // remove read lock +} + +/** +* A named typle assigning a log [`LogVerbosity`] to a [`LogMessageType`] +*/ +pub struct LogMessage(LogMessageType, LogVerbosity); + +impl Display for LogMessage +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> + { + self.0.fmt(f) // just display LogMessageType + } +} + +/** +* Every possible Message, which may be logged. Grouped the following: +* +* These groups correspond to the four levels of logging verbosity. See [`LogVerbosity`]. +*/ +#[derive(Clone, Debug)] +pub enum LogMessageType { + /// Errors + /// An error of any type; please avoid using this. + GenericErr(String), + /// Warnings + /// A warning of any type; please avoid using this. + GenericWarn(String), + /// Info + /// Some information of any type; please avoid using this. + GenericInfo(String), + /// Debug + /// a debug message of any type; please avoid using this. + GenericDebug(String), +} + +/* +* Please don't put anything other except new match arms below this comment, since it is expected that +* the following code will have a lot of lines, due to exhausting all possible enum values +* respectively and (hopefully) detailed messages. +*/ + +impl LogMessageType +{ + /// Returns a new [`LogMessage`] based on the default verbosity of the given [`LogMessageType`]. + pub fn log_message(&self) -> LogMessage + { + let verbosity = match self + { + GenericErr(_) => LogVerbosity::Error, + GenericWarn(_) => LogVerbosity::Warning, + GenericInfo(_) => LogVerbosity::Information, + GenericDebug(_) => LogVerbosity::Debugging, + }; + + LogMessage(self.clone(), verbosity) + } +} + +impl Display for LogMessageType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> + { + match self + { + GenericErr(err) => { write!(f, "Generic Error: {err}") }, + GenericWarn(warn) => { write!(f, "Generic Warning: {warn}") }, + GenericInfo(info) => { write!(f, "Generic Information: {info}") }, + GenericDebug(debug) => { write!(f, "Generic Debug Message: {debug}") }, + } + } +} -- 2.45.2 From f7616943dc86f5e0726858ca9debbff8eeffb9f8 Mon Sep 17 00:00:00 2001 From: Leon Wilzer Date: Tue, 13 Feb 2024 18:40:31 +0100 Subject: [PATCH 04/27] Adjusted some lint options --- Cargo.toml | 7 ++++--- config/Cargo.toml | 15 ++++++++++++++- logging/Cargo.toml | 13 +++++++++++++ 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4e7f53f..d645556 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,12 +11,13 @@ edition = "2021" [lints.rust] unsafe_code = "forbid" +missing_docs = "warn" [lints.clippy] enum_glob_use = "deny" # https://rust-lang.github.io/rust-clippy/master/index.html#/enum_glob_use pedantic = "deny" # https://rust-lang.github.io/rust-clippy/master/index.html#/?groups=pedantic nursery = "deny" # wip lints: https://rust-lang.github.io/rust-clippy/master/index.html#/?groups=nursery -unwrap_used = "deny" # https://rust-lang.github.io/rust-clippy/master/index.html#/unwrap_used +unwrap_used = "forbid" # https://rust-lang.github.io/rust-clippy/master/index.html#/unwrap_used missing_const_for_fn = "warn" # https://rust-lang.github.io/rust-clippy/master/index.html#/missing_const_for_fn -missing_assert_message = "deny" # https://rust-lang.github.io/rust-clippy/master/index.html#/missing_assert_message -missing_errors_doc = "deny" # https://rust-lang.github.io/rust-clippy/master/index.html#/missing_assert_message +missing_assert_message = "warn" # https://rust-lang.github.io/rust-clippy/master/index.html#/missing_assert_message +missing_errors_doc = "warn" # https://rust-lang.github.io/rust-clippy/master/index.html#/missing_assert_message diff --git a/config/Cargo.toml b/config/Cargo.toml index e588f30..d915604 100644 --- a/config/Cargo.toml +++ b/config/Cargo.toml @@ -6,4 +6,17 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -getset = "0.1.2" +getset = { version = "0.1.2", default-features = false } + +[lints.rust] +unsafe_code = "forbid" +missing_docs = "warn" + +[lints.clippy] +enum_glob_use = "deny" # https://rust-lang.github.io/rust-clippy/master/index.html#/enum_glob_use +pedantic = "deny" # https://rust-lang.github.io/rust-clippy/master/index.html#/?groups=pedantic +nursery = "deny" # wip lints: https://rust-lang.github.io/rust-clippy/master/index.html#/?groups=nursery +unwrap_used = "forbid" # https://rust-lang.github.io/rust-clippy/master/index.html#/unwrap_used +missing_const_for_fn = "warn" # https://rust-lang.github.io/rust-clippy/master/index.html#/missing_const_for_fn +missing_assert_message = "warn" # https://rust-lang.github.io/rust-clippy/master/index.html#/missing_assert_message +missing_errors_doc = "warn" # https://rust-lang.github.io/rust-clippy/master/index.html#/missing_assert_message diff --git a/logging/Cargo.toml b/logging/Cargo.toml index dbe95c9..ead6bc1 100644 --- a/logging/Cargo.toml +++ b/logging/Cargo.toml @@ -8,3 +8,16 @@ edition = "2021" [dependencies] chrono = { version = "0.4.34", default-features = false, features = ["now"] } config = { path = "../config"} + +[lints.rust] +unsafe_code = "forbid" +missing_docs = "warn" + +[lints.clippy] +enum_glob_use = "deny" # https://rust-lang.github.io/rust-clippy/master/index.html#/enum_glob_use +pedantic = "deny" # https://rust-lang.github.io/rust-clippy/master/index.html#/?groups=pedantic +nursery = "deny" # wip lints: https://rust-lang.github.io/rust-clippy/master/index.html#/?groups=nursery +unwrap_used = "forbid" # https://rust-lang.github.io/rust-clippy/master/index.html#/unwrap_used +missing_const_for_fn = "warn" # https://rust-lang.github.io/rust-clippy/master/index.html#/missing_const_for_fn +missing_assert_message = "warn" # https://rust-lang.github.io/rust-clippy/master/index.html#/missing_assert_message +missing_errors_doc = "warn" # https://rust-lang.github.io/rust-clippy/master/index.html#/missing_assert_message -- 2.45.2 From f3b061a637c06bc3ada7a8b05ab9e892aa0433e9 Mon Sep 17 00:00:00 2001 From: Leon Wilzer Date: Tue, 13 Feb 2024 21:09:03 +0100 Subject: [PATCH 05/27] rustfmt --- config/src/lib.rs | 180 +++++++++++++++++++++++---------------------- logging/src/lib.rs | 158 ++++++++++++++++++++------------------- 2 files changed, 175 insertions(+), 163 deletions(-) diff --git a/config/src/lib.rs b/config/src/lib.rs index 7248f66..a9f54e3 100644 --- a/config/src/lib.rs +++ b/config/src/lib.rs @@ -16,92 +16,105 @@ use getset::Setters; * Represents a valid `WANessa` configuration. Intended as a read-only singleton. * See [`DEFAULTS`] */ -#[derive(Clone,PartialEq, Eq)] -#[derive(Getters, Setters)] -#[getset(get = "pub")] +#[derive(Clone, PartialEq, Eq, Getters, Setters)] +#[getset(get = "pub", set = "pub")] #[allow(clippy::struct_excessive_bools)] // False positive, since it is a config struct. pub struct Config { /// See [`LogVerbosity`].
- /// Default: [`Warning`] - pub log_verbosity: LogVerbosity, + /// Default: [`Warning`] + log_verbosity: LogVerbosity, /// Logs UTC time and date of message, if true.
- /// Default: `false` - pub log_time: bool, - /// Time and date format.
- /// Defaults to `%F/%T:%f`.
- /// See [chrono](https://docs.rs/chrono/latest/chrono/format/strftime/index.html).
- /// Default example : `2001-12-31/23:59:33:26490000` + /// Default: `false` + log_time: bool, + /// Time and date format.
+ /// Defaults to `%F/%T:%f`.
+ /// See [chrono](https://docs.rs/chrono/latest/chrono/format/strftime/index.html).
+ /// Default example : `2001-12-31/23:59:33:26490000` #[getset(skip)] - pub log_time_format: Option, + log_time_format: Option, /// Logs location in code, where the message was logged, if true.
- /// Default: `false` - pub log_location: bool, - /// If `Some(path)` tries to also write the log to `path` in addition to stderr/stderr.
- /// Default: [None] - pub log_path: Option, - /// Logs to standard out, if true.
- /// Default: `true` - pub log_stdout: bool, - /// Logs to standard err, if true.
- /// Default: `true` - pub log_stderr: bool, + /// Default: `false` + log_location: bool, + /// If `Some(path)` tries to also write the log to `path` in addition to stderr/stderr.
+ /// Default: [None] + log_path: Option, + /// Logs to standard out, if true.
+ /// Default: `true` + log_stdout: bool, + /// Logs to standard err, if true.
+ /// Default: `true` + log_stderr: bool, /// Database connection address. - /// Is an option to allow constructing a default config during compile time.
- /// Default: `localhost`. + /// Is an option to allow constructing a default config during compile time.
+ /// Default: `localhost`. #[getset(skip)] - pub db_addr: Option, + db_addr: Option, /// Database connection port.
- /// Default: `6969` (nice). - pub db_port: u16, + /// Default: `6969` (nice). + db_port: u16, } impl Config { - /// Getter for [`Self::db_addr`]. + /// Getter for [`Self::db_addr`]. + #[must_use] pub fn db_addr(&self) -> &str { - self.db_addr.as_ref().map_or("localhost", |addr| addr) + self.db_addr.as_ref().map_or("localhost", |addr| addr) } - /// Getter for the [`Self::log_time_format`]. - pub fn log_time_format(&self) -> &str - { - self.log_time_format.as_ref().map_or("%F-%T:%f", |fmt| fmt) - } + /// Setter for [`Self::db_addr`]. + pub fn set_db_addr(&mut self, db_addr: impl Into) { + self.db_addr = Some(db_addr.into()); + } + + /// Getter for [`Self::log_time_format`]. + #[must_use] + pub fn log_time_format(&self) -> &str { + todo!(); + // self.log_time_format.as_ref().map_or("%F-%T:%f", |fmt| fmt) + } + + /// Setter for [`Self::log_time_format`]. + pub fn set_log_time_format(&mut self, format: impl Into) { + self.log_time_format = Some(format.into()); + } } /// See [`DEFAULTS`]. impl Default for Config { - /// See [`DEFAULTS`]. + /// See [`DEFAULTS`]. fn default() -> Self { DEFAULTS } } -/// Default configuration. -/// ```rust -/// # use config::Config; -/// # use config::LogVerbosity::Warning; -/// let DEFAULTS = Config -/// { -/// log_verbosity: Warning, -/// log_time: false, -/// log_time_format: None, -/// log_location: false, -/// log_stdout: true, -/// log_stderr: true, -/// log_path: None, -/// db_addr: None, -/// db_port: 6969, -/// }; -/// # assert!(DEFAULTS == config::DEFAULTS) -/// ``` +/** +* Default configuration. +* ```rust +* # use config::Config; +* # use config::LogVerbosity::Warning; +* let DEFAULTS = Config +* { +* log_verbosity: Warning, +* log_time: false, +* log_time_format: None, +* log_location: false, +* log_stdout: true, +* log_stderr: true, +* log_path: None, +* db_addr: None, +* db_port: 6969, +* }; +* # assert!(DEFAULTS == config::DEFAULTS) +* ``` +*/ pub const DEFAULTS: Config = Config { log_verbosity: Warning, log_time: false, - log_time_format: None, + log_time_format: None, log_location: false, - log_stdout: true, - log_stderr: true, - log_path: None, + log_stdout: true, + log_stderr: true, + log_path: None, db_addr: None, db_port: 6969, }; @@ -113,36 +126,31 @@ pub static CONFIG: RwLock = RwLock::new(DEFAULTS); * Each level includes the previous ones. */ #[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum LogVerbosity -{ - /// Critical Errors, may lead to crashes or deactivating certain features. - Error, - /// Very minor and recovered errors, such as invalid configs. - Warning, - /// Very verbose and detailed. Basically gives a step-by-step instruction on what is currently done. - Information, - /// Very technical and even more verbose. - /// May contain secrets and private information. - /// **Do not use in production environments!** - Debugging, +pub enum LogVerbosity { + /// Critical Errors, may lead to crashes or deactivating certain features. + Error, + /// Very minor and recovered errors, such as invalid configs. + Warning, + /// Very verbose and detailed. Basically gives a step-by-step instruction on what is currently done. + Information, + /// Very technical and even more verbose. + /// May contain secrets and private information. + /// **Do not use in production environments!** + Debugging, } -impl PartialOrd for LogVerbosity -{ - /// Some operator overloading of comparison symbols (==, <,>=, etc.) as syntactic sugar. - /// See [`PartialOrd`]. - fn partial_cmp(&self, other: &Self) -> Option - { - (*self as usize).partial_cmp(&(*other as usize)) - } +impl PartialOrd for LogVerbosity { + /// Some operator overloading of comparison symbols (==, <,>=, etc.) as syntactic sugar. + /// See [`PartialOrd`]. + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } } -impl Ord for LogVerbosity -{ - /// Some operator overloading of comparison symbols (==, <,>=, etc.) as syntactic sugar. - /// See [`Ord`]. - fn cmp(&self, other: &Self) -> Ordering - { - (*self as usize).cmp(&(*other as usize)) - } +impl Ord for LogVerbosity { + /// Some operator overloading of comparison symbols (==, <,>=, etc.) as syntactic sugar. + /// See [`Ord`]. + fn cmp(&self, other: &Self) -> Ordering { + (*self as usize).cmp(&(*other as usize)) + } } diff --git a/logging/src/lib.rs b/logging/src/lib.rs index 0d429ec..8701486 100644 --- a/logging/src/lib.rs +++ b/logging/src/lib.rs @@ -11,13 +11,13 @@ use config::LogVerbosity; use chrono::Utc; -use LogVerbosity::Warning; use LogVerbosity::Information; +use LogVerbosity::Warning; -use LogMessageType::GenericErr; -use LogMessageType::GenericWarn; -use LogMessageType::GenericInfo; use LogMessageType::GenericDebug; +use LogMessageType::GenericErr; +use LogMessageType::GenericInfo; +use LogMessageType::GenericWarn; /** * Logs the given message. @@ -27,73 +27,73 @@ use LogMessageType::GenericDebug; * or if another error occurs, such as a full disk. */ // TODO: Create macro to make last three parameters optional. -pub fn log_message(msg: &LogMessage, file: &str, line: &str, column: &str) -{ - let conf = config::CONFIG.read().expect(&format!("Failed aqcuire read lock on config\nTried to log message: {msg}")); +pub fn log_message(msg: &LogMessage, file: &str, line: &str, column: &str) { + let conf = config::CONFIG.read().unwrap_or_else(|_| { + panic!("Failed aqcuire read lock on config\nTried to log message: {msg}") + }); - if conf.log_verbosity() < &msg.1 { return; } + if conf.log_verbosity() < &msg.1 { + return; + } - let mut log_line = String::new(); + let mut log_line = String::new(); - // add time substring - if *conf.log_time() - { - log_line += &format!("{} ", Utc::now().format(conf.log_time_format())); - } + // add time substring + if *conf.log_time() { + log_line += &format!("{} ", Utc::now().format(conf.log_time_format())); + } - // add code location substring - if *conf.log_location() - { - log_line += &format!("{file}:{line},{column} "); - } + // add code location substring + if *conf.log_location() { + log_line += &format!("{file}:{line},{column} "); + } - log_line += &msg.to_string(); + log_line += &msg.to_string(); - // May panic if file cannot be opened or written to. - conf.log_path().as_ref().map_or_else( - || {}, |path| - { - let mut file = OpenOptions::new() - .write(true) - .append(true) - .open(path) - .expect( - &format!("Could not open log file: {path:#?}") - ); - writeln!(file, "{log_line}").expect(&format!("Could not write log to file: {path:#?}")); - } - ); + // May panic if file cannot be opened or written to. + conf.log_path().as_ref().map_or_else( + || {}, + |path| { + let mut file = OpenOptions::new() + .write(true) + .append(true) + .open(path) + .unwrap_or_else(|_| { + panic!( + "Could not open log fil + e: {path:#?}" + ) + }); + writeln!(file, "{log_line}") + .unwrap_or_else(|_| panic!("Could not write log to file: {path:#?}")); + }, + ); - if msg.1 <= Warning && *conf.log_stderr() - { - // May panic if writing to stderr fails. - eprintln!("{log_line}"); - } - else if msg.1 >= Information && *conf.log_stdout() - { - // May panic if writing to stdout fails. - println!("{log_line}"); - } + if msg.1 <= Warning && *conf.log_stderr() { + // May panic if writing to stderr fails. + eprintln!("{log_line}"); + } else if msg.1 >= Information && *conf.log_stdout() { + // May panic if writing to stdout fails. + println!("{log_line}"); + } - drop(conf); // remove read lock + drop(conf); // remove read lock } /** -* A named typle assigning a log [`LogVerbosity`] to a [`LogMessageType`] +* A named typle assigning a log [`LogVerbosity`] to a [`LogMessageType`] */ pub struct LogMessage(LogMessageType, LogVerbosity); -impl Display for LogMessage -{ - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> - { - self.0.fmt(f) // just display LogMessageType - } +impl Display for LogMessage { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { + self.0.fmt(f) // just display LogMessageType + } } /** * Every possible Message, which may be logged. Grouped the following: -* +* * These groups correspond to the four levels of logging verbosity. See [`LogVerbosity`]. */ #[derive(Clone, Debug)] @@ -118,32 +118,36 @@ pub enum LogMessageType { * respectively and (hopefully) detailed messages. */ -impl LogMessageType -{ - /// Returns a new [`LogMessage`] based on the default verbosity of the given [`LogMessageType`]. - pub fn log_message(&self) -> LogMessage - { - let verbosity = match self - { - GenericErr(_) => LogVerbosity::Error, - GenericWarn(_) => LogVerbosity::Warning, - GenericInfo(_) => LogVerbosity::Information, - GenericDebug(_) => LogVerbosity::Debugging, - }; +impl LogMessageType { + /// Returns a new [`LogMessage`] based on the default verbosity of the given [`LogMessageType`]. + #[must_use] + pub fn log_message(&self) -> LogMessage { + let verbosity = match self { + GenericErr(_) => LogVerbosity::Error, + GenericWarn(_) => LogVerbosity::Warning, + GenericInfo(_) => LogVerbosity::Information, + GenericDebug(_) => LogVerbosity::Debugging, + }; - LogMessage(self.clone(), verbosity) - } + LogMessage(self.clone(), verbosity) + } } impl Display for LogMessageType { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> - { - match self - { - GenericErr(err) => { write!(f, "Generic Error: {err}") }, - GenericWarn(warn) => { write!(f, "Generic Warning: {warn}") }, - GenericInfo(info) => { write!(f, "Generic Information: {info}") }, - GenericDebug(debug) => { write!(f, "Generic Debug Message: {debug}") }, - } - } + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { + match self { + GenericErr(err) => { + write!(f, "Generic Error: {err}") + } + GenericWarn(warn) => { + write!(f, "Generic Warning: {warn}") + } + GenericInfo(info) => { + write!(f, "Generic Information: {info}") + } + GenericDebug(debug) => { + write!(f, "Generic Debug Message: {debug}") + } + } + } } -- 2.45.2 From a54a226cefca80b5f10a7a222f0e1350aa408085 Mon Sep 17 00:00:00 2001 From: Leon Wilzer Date: Wed, 14 Feb 2024 20:52:31 +0100 Subject: [PATCH 06/27] macros batman commit --- Cargo.toml | 2 +- macros/Cargo.toml | 24 ++++++++++++++++++++++++ macros/src/lib.rs | 0 3 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 macros/Cargo.toml create mode 100644 macros/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index d645556..2c54a78 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,4 +1,4 @@ -workspace = { members = ["config", "logging"] } +workspace = { members = ["config", "logging", "macros"] } [package] name = "wanessa" diff --git a/macros/Cargo.toml b/macros/Cargo.toml new file mode 100644 index 0000000..c9310a6 --- /dev/null +++ b/macros/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "macros" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +[lib] +proc-macro = true + +[lints.rust] +unsafe_code = "forbid" +missing_docs = "warn" + +[lints.clippy] +enum_glob_use = "deny" # https://rust-lang.github.io/rust-clippy/master/index.html#/enum_glob_use +pedantic = "deny" # https://rust-lang.github.io/rust-clippy/master/index.html#/?groups=pedantic +nursery = "deny" # wip lints: https://rust-lang.github.io/rust-clippy/master/index.html#/?groups=nursery +unwrap_used = "forbid" # https://rust-lang.github.io/rust-clippy/master/index.html#/unwrap_used +missing_const_for_fn = "warn" # https://rust-lang.github.io/rust-clippy/master/index.html#/missing_const_for_fn +missing_assert_message = "warn" # https://rust-lang.github.io/rust-clippy/master/index.html#/missing_assert_message +missing_errors_doc = "warn" # https://rust-lang.github.io/rust-clippy/master/index.html#/missing_assert_message diff --git a/macros/src/lib.rs b/macros/src/lib.rs new file mode 100644 index 0000000..e69de29 -- 2.45.2 From d8b7616c43499f930f8df1849ff81c5dd1553e8b Mon Sep 17 00:00:00 2001 From: Leon Wilzer Date: Wed, 14 Feb 2024 21:12:30 +0100 Subject: [PATCH 07/27] fixed doc example --- config/src/lib.rs | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/config/src/lib.rs b/config/src/lib.rs index a9f54e3..c6a817b 100644 --- a/config/src/lib.rs +++ b/config/src/lib.rs @@ -16,9 +16,9 @@ use getset::Setters; * Represents a valid `WANessa` configuration. Intended as a read-only singleton. * See [`DEFAULTS`] */ -#[derive(Clone, PartialEq, Eq, Getters, Setters)] +#[derive(Clone, PartialEq, Eq, Getters, Setters, Debug)] #[getset(get = "pub", set = "pub")] -#[allow(clippy::struct_excessive_bools)] // False positive, since it is a config struct. +#[allow(clippy::struct_excessive_bools)] // False positive, since this is a config struct. pub struct Config { /// See [`LogVerbosity`].
/// Default: [`Warning`] @@ -92,19 +92,19 @@ impl Default for Config { * ```rust * # use config::Config; * # use config::LogVerbosity::Warning; -* let DEFAULTS = Config -* { -* log_verbosity: Warning, -* log_time: false, -* log_time_format: None, -* log_location: false, -* log_stdout: true, -* log_stderr: true, -* log_path: None, -* db_addr: None, -* db_port: 6969, -* }; -* # assert!(DEFAULTS == config::DEFAULTS) +* let mut defaults = Config::default(); +* +* defaults.set_log_verbosity(Warning); +* defaults.set_log_time(false); +* //defaults.set_log_time_format(None); +* defaults.set_log_location(false); +* defaults.set_log_stdout(true); +* defaults.set_log_stderr(true); +* //defaults.set_log_path(None); +* //defaults.set_db_addr(None); +* defaults.set_db_port(6969); +* +* # assert!(defaults == config::DEFAULTS) * ``` */ pub const DEFAULTS: Config = Config { -- 2.45.2 From ef9c2d4fd9a51aaa68174a7c9ba2e078b3479027 Mon Sep 17 00:00:00 2001 From: Leon Wilzer Date: Wed, 14 Feb 2024 20:13:57 +0000 Subject: [PATCH 08/27] rustfmt --- config/src/lib.rs | 2 +- macros/src/lib.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/config/src/lib.rs b/config/src/lib.rs index c6a817b..dad9eba 100644 --- a/config/src/lib.rs +++ b/config/src/lib.rs @@ -103,7 +103,7 @@ impl Default for Config { * //defaults.set_log_path(None); * //defaults.set_db_addr(None); * defaults.set_db_port(6969); -* +* * # assert!(defaults == config::DEFAULTS) * ``` */ diff --git a/macros/src/lib.rs b/macros/src/lib.rs index e69de29..8b13789 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -0,0 +1 @@ + -- 2.45.2 From 9f91876a4f456564006dc71cd81c9e30ec1add08 Mon Sep 17 00:00:00 2001 From: Leon Wilzer Date: Thu, 15 Feb 2024 01:22:10 +0100 Subject: [PATCH 09/27] macros sub crate not yet needed --- Cargo.toml | 2 +- macros/Cargo.toml | 24 ------------------------ macros/src/lib.rs | 1 - 3 files changed, 1 insertion(+), 26 deletions(-) delete mode 100644 macros/Cargo.toml delete mode 100644 macros/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 2c54a78..d645556 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,4 +1,4 @@ -workspace = { members = ["config", "logging", "macros"] } +workspace = { members = ["config", "logging"] } [package] name = "wanessa" diff --git a/macros/Cargo.toml b/macros/Cargo.toml deleted file mode 100644 index c9310a6..0000000 --- a/macros/Cargo.toml +++ /dev/null @@ -1,24 +0,0 @@ -[package] -name = "macros" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] - -[lib] -proc-macro = true - -[lints.rust] -unsafe_code = "forbid" -missing_docs = "warn" - -[lints.clippy] -enum_glob_use = "deny" # https://rust-lang.github.io/rust-clippy/master/index.html#/enum_glob_use -pedantic = "deny" # https://rust-lang.github.io/rust-clippy/master/index.html#/?groups=pedantic -nursery = "deny" # wip lints: https://rust-lang.github.io/rust-clippy/master/index.html#/?groups=nursery -unwrap_used = "forbid" # https://rust-lang.github.io/rust-clippy/master/index.html#/unwrap_used -missing_const_for_fn = "warn" # https://rust-lang.github.io/rust-clippy/master/index.html#/missing_const_for_fn -missing_assert_message = "warn" # https://rust-lang.github.io/rust-clippy/master/index.html#/missing_assert_message -missing_errors_doc = "warn" # https://rust-lang.github.io/rust-clippy/master/index.html#/missing_assert_message diff --git a/macros/src/lib.rs b/macros/src/lib.rs deleted file mode 100644 index 8b13789..0000000 --- a/macros/src/lib.rs +++ /dev/null @@ -1 +0,0 @@ - -- 2.45.2 From a3fe0d068622bb3792f380eb890829773c6ab15f Mon Sep 17 00:00:00 2001 From: Leon Wilzer Date: Thu, 15 Feb 2024 01:33:14 +0100 Subject: [PATCH 10/27] some small fixes --- config/src/lib.rs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/config/src/lib.rs b/config/src/lib.rs index dad9eba..2a705dc 100644 --- a/config/src/lib.rs +++ b/config/src/lib.rs @@ -2,6 +2,7 @@ * A singleton, thread-safe struct used for accessing */ +use std::path::Path; use std::path::PathBuf; use std::sync::RwLock; @@ -37,6 +38,7 @@ pub struct Config { log_location: bool, /// If `Some(path)` tries to also write the log to `path` in addition to stderr/stderr.
/// Default: [None] + #[getset(skip)] log_path: Option, /// Logs to standard out, if true.
/// Default: `true` @@ -69,14 +71,26 @@ impl Config { /// Getter for [`Self::log_time_format`]. #[must_use] pub fn log_time_format(&self) -> &str { - todo!(); - // self.log_time_format.as_ref().map_or("%F-%T:%f", |fmt| fmt) + self.log_time_format.as_ref().map_or("%F-%T:%f", |fmt| fmt) } /// Setter for [`Self::log_time_format`]. pub fn set_log_time_format(&mut self, format: impl Into) { self.log_time_format = Some(format.into()); } + + /// Getter for [`Self::log_path`]. + #[must_use] + pub fn log_path(&self) -> Option<&Path> + { + self.log_path.as_deref() + } + + /// Setter for [`Self::log_path`]. + pub fn set_log_path(&mut self, log_path: impl Into>) + { + self.log_path = log_path.into(); + } } /// See [`DEFAULTS`]. -- 2.45.2 From 1dc9f120d6cf242c0e390956d1e060d3728a61b0 Mon Sep 17 00:00:00 2001 From: Leon Wilzer Date: Thu, 15 Feb 2024 01:36:49 +0100 Subject: [PATCH 11/27] doc test tweaks --- config/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/config/src/lib.rs b/config/src/lib.rs index 2a705dc..97b3f27 100644 --- a/config/src/lib.rs +++ b/config/src/lib.rs @@ -110,15 +110,15 @@ impl Default for Config { * * defaults.set_log_verbosity(Warning); * defaults.set_log_time(false); -* //defaults.set_log_time_format(None); +* assert_eq!(defaults.log_time_format(), "%F-%T:%f"); * defaults.set_log_location(false); * defaults.set_log_stdout(true); * defaults.set_log_stderr(true); -* //defaults.set_log_path(None); -* //defaults.set_db_addr(None); +* defaults.set_log_path(None); +* assert_eq!(defaults.db_addr(), "localhost"); * defaults.set_db_port(6969); * -* # assert!(defaults == config::DEFAULTS) +* # assert_eq!(defaults,config::DEFAULTS) * ``` */ pub const DEFAULTS: Config = Config { -- 2.45.2 From 2580886d04c3d55dfa45b78b30790a339b3842a6 Mon Sep 17 00:00:00 2001 From: Leon Wilzer Date: Thu, 15 Feb 2024 02:44:56 +0100 Subject: [PATCH 12/27] debugging and some unit tests --- logging/Cargo.toml | 5 + logging/src/lib.rs | 76 ++++++++----- logging/src/test.rs | 262 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 317 insertions(+), 26 deletions(-) create mode 100644 logging/src/test.rs diff --git a/logging/Cargo.toml b/logging/Cargo.toml index ead6bc1..99b9e87 100644 --- a/logging/Cargo.toml +++ b/logging/Cargo.toml @@ -9,6 +9,11 @@ edition = "2021" chrono = { version = "0.4.34", default-features = false, features = ["now"] } config = { path = "../config"} +[dev-dependencies] +uuid = { version = "1.7.0", default-features = false, features = ["v4", "fast-rng"] } +once_cell = { version = "1.19.0", default-features = false, features = ["std"] } +futures = { version = "0.3.30", default-features = false, features = ["alloc", "executor"] } + [lints.rust] unsafe_code = "forbid" missing_docs = "warn" diff --git a/logging/src/lib.rs b/logging/src/lib.rs index 8701486..d795636 100644 --- a/logging/src/lib.rs +++ b/logging/src/lib.rs @@ -3,11 +3,14 @@ * It should mostly be called statically. */ +mod test; + use std::fmt::Display; use std::fs::OpenOptions; use std::io::Write; use config::LogVerbosity; +use config::Config; use chrono::Utc; @@ -26,29 +29,9 @@ use LogMessageType::GenericWarn; * Panics if readlock on [`config::CONFIG`] could not be acquired * or if another error occurs, such as a full disk. */ -// TODO: Create macro to make last three parameters optional. -pub fn log_message(msg: &LogMessage, file: &str, line: &str, column: &str) { - let conf = config::CONFIG.read().unwrap_or_else(|_| { - panic!("Failed aqcuire read lock on config\nTried to log message: {msg}") - }); +pub fn log_message(msg: &LogMessage, conf: &Config, file: &str, line: u32, column: u32) { - if conf.log_verbosity() < &msg.1 { - return; - } - - let mut log_line = String::new(); - - // add time substring - if *conf.log_time() { - log_line += &format!("{} ", Utc::now().format(conf.log_time_format())); - } - - // add code location substring - if *conf.log_location() { - log_line += &format!("{file}:{line},{column} "); - } - - log_line += &msg.to_string(); + let Some(log_line) = log_to_str(msg, conf, file, line, column) else { return }; // May panic if file cannot be opened or written to. conf.log_path().as_ref().map_or_else( @@ -57,11 +40,11 @@ pub fn log_message(msg: &LogMessage, file: &str, line: &str, column: &str) { let mut file = OpenOptions::new() .write(true) .append(true) + .create(true) .open(path) .unwrap_or_else(|_| { panic!( - "Could not open log fil - e: {path:#?}" + "Could not open log file: {path:#?}" ) }); writeln!(file, "{log_line}") @@ -76,13 +59,54 @@ pub fn log_message(msg: &LogMessage, file: &str, line: &str, column: &str) { // May panic if writing to stdout fails. println!("{log_line}"); } +} - drop(conf); // remove read lock +/** +* Return log line, if message may be logged according to [`config::Config`]. +* # Panics +* Panics if readlock on [`config::CONFIG`] could not be acquired +* or if another error occurs, such as a full disk. +*/ +#[must_use] +pub fn log_to_str(msg: &LogMessage, conf: &Config, file: &str, line: u32, column: u32) -> Option +{ + if conf.log_verbosity() < &msg.1 { + return None; + } + + let mut log_line = String::new(); + + // add time substring + if *conf.log_time() { + log_line += &format!("{} ", Utc::now().format(conf.log_time_format())); + } + + // add code location substring + if *conf.log_location() { + log_line += &format!("{file}:{line},{column} "); + } + + Some(log_line + &msg.to_string()) +} + +/** +* Shorthand version for [`log_message`], which does not require information about the [`config::Config::log_location`]. +*/ +#[macro_export] +macro_rules! log{ + ($msg:expr) => { + let conf = config::CONFIG.read().unwrap_or_else(|_| { + panic!("Failed aqcuire read lock on config!") + }); + log_message($msg, &*conf, file!(), line!(), column!()); + drop(conf); + } } /** * A named typle assigning a log [`LogVerbosity`] to a [`LogMessageType`] */ +#[derive(PartialEq, Eq, Debug)] pub struct LogMessage(LogMessageType, LogVerbosity); impl Display for LogMessage { @@ -96,7 +120,7 @@ impl Display for LogMessage { * * These groups correspond to the four levels of logging verbosity. See [`LogVerbosity`]. */ -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq, Eq)] pub enum LogMessageType { /// Errors /// An error of any type; please avoid using this. diff --git a/logging/src/test.rs b/logging/src/test.rs new file mode 100644 index 0000000..0b450ed --- /dev/null +++ b/logging/src/test.rs @@ -0,0 +1,262 @@ +#![cfg(test)] +/*! +* This test suite uses uuid to easily avoid race conditions when writing to the same log file. +*/ + +use std::{collections::HashSet, env, fs::{create_dir_all, read_to_string}, mem::take, path::PathBuf}; + +use futures::{executor::block_on, future::join_all}; +use once_cell::sync::Lazy; + +use uuid::Uuid; + +use super::*; + +const CONCURRENT_MESSAGE_COUNT: usize = 99999; + +static LOG_DIR: Lazy = Lazy::new(|| + if cfg!(unix) + { + String::from("/tmp/WANessa/unit-tests/logging") + } + else if cfg !(windows) { + let tmp_path = env::var("TMP").unwrap_or_else(|_| env::var("TEMP").expect("Windows should have both TMP and TEMP, but you have neither, what did you do?")); + format!("{tmp_path}/WANessa/unit-tests/logging") + } + else { String::new() } +); + + + + +/// Tests if [`log_message`] to file correctly. +#[test] +pub fn log_msg_file() +{ + let log_path = &format!("{}/{}", *LOG_DIR, Uuid::new_v4()); + println!("Log Path: {log_path}"); + let message = LogMessageType::GenericWarn(String::from("Test Log")).log_message(); + let mut config = config::DEFAULTS; + config.set_log_path(PathBuf::from(log_path)); + + create_dir_all(PathBuf::from(LOG_DIR.as_str())) + .unwrap_or_else(|_| panic!("Could not create directory: {}", *LOG_DIR)); + + log_message(&message, &config, file!(), line!(), column!()); + + let log_file = read_to_string(PathBuf::from(log_path)) + .unwrap_or_else(|_| panic!("Could not read file: {log_path}")); + + assert_eq!(message.to_string() + "\n", log_file); +} + +/// Tests if [`log_message`] does not modify output from [`log_to_str`], when logging to file. +#[test] +pub fn log_str() +{ + let log_path = &format!("{}/{}", *LOG_DIR, Uuid::new_v4()); + println!("Log Path: {log_path}"); + let message = LogMessageType::GenericWarn(String::from("Test Log")).log_message(); + let mut config = config::DEFAULTS; + config.set_log_path(PathBuf::from(log_path)); + + create_dir_all(PathBuf::from(LOG_DIR.as_str())) + .unwrap_or_else(|_| panic!("Could not create directory: {}", *LOG_DIR)); + + let log_line = log_to_str(&message, &config, file!(), line!(), column!()).expect("There should be a log line.") + "\n"; + + log_message(&message, &config, file!(), line!(), column!()); + + let log_file = read_to_string(PathBuf::from(log_path)) + .unwrap_or_else(|_| panic!("Could not read file: {log_path}")); + + assert_eq!(log_line, log_file); +} + +/// Tests if no messages are unintentionally filtered due to their verboisity. +#[test] +pub fn verbosity_no_filter() +{ + let log_path = &format!("{}/{}", *LOG_DIR, Uuid::new_v4()); + println!("Log Path: {log_path}"); + let messages = vec![ + LogMessageType::GenericErr(String::from("Test Err")).log_message(), + LogMessageType::GenericWarn(String::from("Test Warn")).log_message(), + LogMessageType::GenericInfo(String::from("Test Info")).log_message(), + LogMessageType::GenericDebug(String::from("Test Debug")).log_message(), + ]; + let mut config = config::DEFAULTS; + config.set_log_path(PathBuf::from(log_path)); + config.set_log_verbosity(LogVerbosity::Error); + + create_dir_all(PathBuf::from(LOG_DIR.as_str())) + .unwrap_or_else(|_| panic!("Could not create directory: {}", *LOG_DIR)); + + let log_line = log_to_str(&messages[0], &config, file!(), line!(), column!()).expect("There should be a log line.") + "\n"; + + for msg in messages + { + log_message(&msg, &config, file!(), line!(), column!()); + } + + let log_file = read_to_string(PathBuf::from(log_path)) + .unwrap_or_else(|_| panic!("Could not read file: {log_path}")); + + assert_eq!(log_file, log_line); +} + +/// Tests if messages are properly filtered according to their verbosity. +#[test] +pub fn verbosity_filter() +{ + let log_path = &format!("{}/{}", *LOG_DIR, Uuid::new_v4()); + println!("Log Path: {log_path}"); + let messages = vec![ + LogMessageType::GenericErr(String::from("Test Err")).log_message(), + LogMessageType::GenericWarn(String::from("Test Warn")).log_message(), + LogMessageType::GenericInfo(String::from("Test Info")).log_message(), + LogMessageType::GenericDebug(String::from("Test Debug")).log_message(), + ]; + let mut config = config::DEFAULTS; + config.set_log_path(PathBuf::from(log_path)); + + create_dir_all(PathBuf::from(LOG_DIR.as_str())) + .unwrap_or_else(|_| panic!("Could not create directory: {}", *LOG_DIR)); + + let mut log_line = log_to_str(&messages[0], &config, file!(), line!(), column!()).expect("There should be a log line.") + "\n"; + log_line += &(log_to_str(&messages[1], &config, file!(), line!(), column!()).expect("There should be a log line.") + "\n"); + + for msg in messages + { + log_message(&msg, &config, file!(), line!(), column!()); + } + + let log_file = read_to_string(PathBuf::from(log_path)) + .unwrap_or_else(|_| panic!("Could not read file: {log_path}")); + + assert_eq!(log_file, log_line); +} + + +/** +* All testing concurrency in a controlled manner. +* +* When a test modifies the config according to it's requirements, another test might panic, because +* it might read the result from the changed config. +*/ +#[test] +pub fn concurrency_tests() +{ + log_macro_file(); + log_concurrently_any_order(); + log_concurrently_correct_order(); +} + +/** +* Tests if log macro logs to file correctly. +* [`config::CONFIG`] +*/ +fn log_macro_file() +{ + let log_path = &format!("{}/{}", *LOG_DIR, Uuid::new_v4()); + println!("Log Path: {log_path}"); + let message = LogMessageType::GenericWarn(String::from("Test Log")).log_message(); + let mut config = config::CONFIG.write().expect("Could not acquire write lock on config!"); + take(&mut *config); + config.set_log_path(PathBuf::from(log_path)); + drop(config); + + create_dir_all(PathBuf::from(LOG_DIR.as_str())) + .unwrap_or_else(|_| panic!("Could not create directory: {}", *LOG_DIR)); + + log!(&message); + + let log_file = read_to_string(PathBuf::from(log_path)) + .unwrap_or_else(|_| panic!("Could not read file: {log_path}")); + + assert_eq!(message.to_string()+"\n", log_file); +} + +/** +* Tests concurrent logging. Log lines may be in any order. +*/ +fn log_concurrently_any_order() +{ + let log_path = &format!("{}/{}", *LOG_DIR, Uuid::new_v4()); + println!("Log Path: {log_path}"); + let mut config = config::CONFIG.write().expect("Could not acquire write lock on config!"); + take(&mut *config); + let mut messages = Vec::with_capacity(CONCURRENT_MESSAGE_COUNT); + config.set_log_path(PathBuf::from(log_path)); + drop(config); + + for i in 0..CONCURRENT_MESSAGE_COUNT + { + let msg = i.to_string(); + messages.push( + async { + log!(&LogMessageType::GenericWarn(msg).log_message()); + } + ); + } + + block_on(join_all(messages)); + + let mut num_set = HashSet::with_capacity(CONCURRENT_MESSAGE_COUNT); + for i in 0..CONCURRENT_MESSAGE_COUNT + { + num_set.insert(i); + } + + for line in read_to_string(PathBuf::from(log_path)).unwrap_or_else(|_| panic!("Could not read file: {log_path}")).lines() + { + let num_str = line.split_whitespace().last().unwrap_or_else(|| panic!("Could not get message number from line: {line}")); + let num = usize::from_str_radix(num_str, 10).unwrap_or_else(|_| panic!("Could not parse number: {num_str}")); + assert!(num_set.remove(&num)); + } + + assert_eq!(num_set.len(), 0); +} + +/** +* Tests concurrent logging. Log lines must be in order. +*/ +fn log_concurrently_correct_order() +{ + let log_path = &format!("{}/{}", *LOG_DIR, Uuid::new_v4()); + println!("Log Path: {log_path}"); + let mut config = config::CONFIG.write().expect("Could not acquire write lock on config!"); + take(&mut *config); + let mut messages = Vec::with_capacity(CONCURRENT_MESSAGE_COUNT); + config.set_log_path(PathBuf::from(log_path)); + drop(config); + + for i in 0..CONCURRENT_MESSAGE_COUNT + { + let msg = i.to_string(); + messages.push( + async { + log!(&LogMessageType::GenericWarn(msg).log_message()); + } + ); + } + + block_on(join_all(messages)); + + let mut num_set = HashSet::with_capacity(CONCURRENT_MESSAGE_COUNT); + for i in 0..CONCURRENT_MESSAGE_COUNT + { + num_set.insert(i); + } + + for (i, line) in read_to_string(PathBuf::from(log_path)).unwrap_or_else(|_| panic!("Could not read file: {log_path}")).lines().enumerate() + { + let num_str = line.split_whitespace().last().unwrap_or_else(|| panic!("Could not get message number from line: {line}")); + let num = usize::from_str_radix(num_str, 10).unwrap_or_else(|_| panic!("Could not parse number: {num_str}")); + assert!(num_set.remove(&num)); + assert_eq!(i, num); + } + + assert_eq!(num_set.len(), 0); + +} -- 2.45.2 From 9110c16256f825de048f84cc46b969e771f1af71 Mon Sep 17 00:00:00 2001 From: Leon Wilzer Date: Thu, 15 Feb 2024 01:46:09 +0000 Subject: [PATCH 13/27] rustfmt --- config/src/lib.rs | 14 +- logging/src/lib.rs | 40 ++--- logging/src/test.rs | 359 +++++++++++++++++++++++--------------------- 3 files changed, 212 insertions(+), 201 deletions(-) diff --git a/config/src/lib.rs b/config/src/lib.rs index 97b3f27..e62ac8a 100644 --- a/config/src/lib.rs +++ b/config/src/lib.rs @@ -81,16 +81,14 @@ impl Config { /// Getter for [`Self::log_path`]. #[must_use] - pub fn log_path(&self) -> Option<&Path> - { - self.log_path.as_deref() - } + pub fn log_path(&self) -> Option<&Path> { + self.log_path.as_deref() + } /// Setter for [`Self::log_path`]. - pub fn set_log_path(&mut self, log_path: impl Into>) - { - self.log_path = log_path.into(); - } + pub fn set_log_path(&mut self, log_path: impl Into>) { + self.log_path = log_path.into(); + } } /// See [`DEFAULTS`]. diff --git a/logging/src/lib.rs b/logging/src/lib.rs index d795636..903f2ed 100644 --- a/logging/src/lib.rs +++ b/logging/src/lib.rs @@ -9,8 +9,8 @@ use std::fmt::Display; use std::fs::OpenOptions; use std::io::Write; -use config::LogVerbosity; use config::Config; +use config::LogVerbosity; use chrono::Utc; @@ -30,8 +30,7 @@ use LogMessageType::GenericWarn; * or if another error occurs, such as a full disk. */ pub fn log_message(msg: &LogMessage, conf: &Config, file: &str, line: u32, column: u32) { - - let Some(log_line) = log_to_str(msg, conf, file, line, column) else { return }; + let Some(log_line) = log_to_str(msg, conf, file, line, column) else { return }; // May panic if file cannot be opened or written to. conf.log_path().as_ref().map_or_else( @@ -40,13 +39,9 @@ pub fn log_message(msg: &LogMessage, conf: &Config, file: &str, line: u32, colum let mut file = OpenOptions::new() .write(true) .append(true) - .create(true) + .create(true) .open(path) - .unwrap_or_else(|_| { - panic!( - "Could not open log file: {path:#?}" - ) - }); + .unwrap_or_else(|_| panic!("Could not open log file: {path:#?}")); writeln!(file, "{log_line}") .unwrap_or_else(|_| panic!("Could not write log to file: {path:#?}")); }, @@ -68,8 +63,13 @@ pub fn log_message(msg: &LogMessage, conf: &Config, file: &str, line: u32, colum * or if another error occurs, such as a full disk. */ #[must_use] -pub fn log_to_str(msg: &LogMessage, conf: &Config, file: &str, line: u32, column: u32) -> Option -{ +pub fn log_to_str( + msg: &LogMessage, + conf: &Config, + file: &str, + line: u32, + column: u32, +) -> Option { if conf.log_verbosity() < &msg.1 { return None; } @@ -86,21 +86,21 @@ pub fn log_to_str(msg: &LogMessage, conf: &Config, file: &str, line: u32, column log_line += &format!("{file}:{line},{column} "); } - Some(log_line + &msg.to_string()) + Some(log_line + &msg.to_string()) } /** * Shorthand version for [`log_message`], which does not require information about the [`config::Config::log_location`]. */ #[macro_export] -macro_rules! log{ - ($msg:expr) => { - let conf = config::CONFIG.read().unwrap_or_else(|_| { - panic!("Failed aqcuire read lock on config!") - }); - log_message($msg, &*conf, file!(), line!(), column!()); - drop(conf); - } +macro_rules! log { + ($msg:expr) => { + let conf = config::CONFIG + .read() + .unwrap_or_else(|_| panic!("Failed aqcuire read lock on config!")); + log_message($msg, &*conf, file!(), line!(), column!()); + drop(conf); + }; } /** diff --git a/logging/src/test.rs b/logging/src/test.rs index 0b450ed..18ff52b 100644 --- a/logging/src/test.rs +++ b/logging/src/test.rs @@ -3,7 +3,13 @@ * This test suite uses uuid to easily avoid race conditions when writing to the same log file. */ -use std::{collections::HashSet, env, fs::{create_dir_all, read_to_string}, mem::take, path::PathBuf}; +use std::{ + collections::HashSet, + env, + fs::{create_dir_all, read_to_string}, + mem::take, + path::PathBuf, +}; use futures::{executor::block_on, future::join_all}; use once_cell::sync::Lazy; @@ -14,130 +20,131 @@ use super::*; const CONCURRENT_MESSAGE_COUNT: usize = 99999; -static LOG_DIR: Lazy = Lazy::new(|| - if cfg!(unix) - { - String::from("/tmp/WANessa/unit-tests/logging") - } - else if cfg !(windows) { - let tmp_path = env::var("TMP").unwrap_or_else(|_| env::var("TEMP").expect("Windows should have both TMP and TEMP, but you have neither, what did you do?")); - format!("{tmp_path}/WANessa/unit-tests/logging") - } - else { String::new() } -); - - - +static LOG_DIR: Lazy = Lazy::new(|| { + if cfg!(unix) { + String::from("/tmp/WANessa/unit-tests/logging") + } else if cfg!(windows) { + let tmp_path = env::var("TMP").unwrap_or_else(|_| { + env::var("TEMP").expect( + "Windows should have both TMP and TEMP, but you have neither, what did you do?", + ) + }); + format!("{tmp_path}/WANessa/unit-tests/logging") + } else { + String::new() + } +}); /// Tests if [`log_message`] to file correctly. #[test] -pub fn log_msg_file() -{ - let log_path = &format!("{}/{}", *LOG_DIR, Uuid::new_v4()); - println!("Log Path: {log_path}"); - let message = LogMessageType::GenericWarn(String::from("Test Log")).log_message(); - let mut config = config::DEFAULTS; - config.set_log_path(PathBuf::from(log_path)); +pub fn log_msg_file() { + let log_path = &format!("{}/{}", *LOG_DIR, Uuid::new_v4()); + println!("Log Path: {log_path}"); + let message = LogMessageType::GenericWarn(String::from("Test Log")).log_message(); + let mut config = config::DEFAULTS; + config.set_log_path(PathBuf::from(log_path)); - create_dir_all(PathBuf::from(LOG_DIR.as_str())) - .unwrap_or_else(|_| panic!("Could not create directory: {}", *LOG_DIR)); + create_dir_all(PathBuf::from(LOG_DIR.as_str())) + .unwrap_or_else(|_| panic!("Could not create directory: {}", *LOG_DIR)); - log_message(&message, &config, file!(), line!(), column!()); + log_message(&message, &config, file!(), line!(), column!()); - let log_file = read_to_string(PathBuf::from(log_path)) - .unwrap_or_else(|_| panic!("Could not read file: {log_path}")); + let log_file = read_to_string(PathBuf::from(log_path)) + .unwrap_or_else(|_| panic!("Could not read file: {log_path}")); - assert_eq!(message.to_string() + "\n", log_file); + assert_eq!(message.to_string() + "\n", log_file); } /// Tests if [`log_message`] does not modify output from [`log_to_str`], when logging to file. #[test] -pub fn log_str() -{ - let log_path = &format!("{}/{}", *LOG_DIR, Uuid::new_v4()); - println!("Log Path: {log_path}"); - let message = LogMessageType::GenericWarn(String::from("Test Log")).log_message(); - let mut config = config::DEFAULTS; - config.set_log_path(PathBuf::from(log_path)); +pub fn log_str() { + let log_path = &format!("{}/{}", *LOG_DIR, Uuid::new_v4()); + println!("Log Path: {log_path}"); + let message = LogMessageType::GenericWarn(String::from("Test Log")).log_message(); + let mut config = config::DEFAULTS; + config.set_log_path(PathBuf::from(log_path)); - create_dir_all(PathBuf::from(LOG_DIR.as_str())) - .unwrap_or_else(|_| panic!("Could not create directory: {}", *LOG_DIR)); + create_dir_all(PathBuf::from(LOG_DIR.as_str())) + .unwrap_or_else(|_| panic!("Could not create directory: {}", *LOG_DIR)); - let log_line = log_to_str(&message, &config, file!(), line!(), column!()).expect("There should be a log line.") + "\n"; + let log_line = log_to_str(&message, &config, file!(), line!(), column!()) + .expect("There should be a log line.") + + "\n"; - log_message(&message, &config, file!(), line!(), column!()); + log_message(&message, &config, file!(), line!(), column!()); - let log_file = read_to_string(PathBuf::from(log_path)) - .unwrap_or_else(|_| panic!("Could not read file: {log_path}")); + let log_file = read_to_string(PathBuf::from(log_path)) + .unwrap_or_else(|_| panic!("Could not read file: {log_path}")); - assert_eq!(log_line, log_file); + assert_eq!(log_line, log_file); } /// Tests if no messages are unintentionally filtered due to their verboisity. #[test] -pub fn verbosity_no_filter() -{ - let log_path = &format!("{}/{}", *LOG_DIR, Uuid::new_v4()); - println!("Log Path: {log_path}"); - let messages = vec![ - LogMessageType::GenericErr(String::from("Test Err")).log_message(), - LogMessageType::GenericWarn(String::from("Test Warn")).log_message(), - LogMessageType::GenericInfo(String::from("Test Info")).log_message(), - LogMessageType::GenericDebug(String::from("Test Debug")).log_message(), - ]; - let mut config = config::DEFAULTS; - config.set_log_path(PathBuf::from(log_path)); - config.set_log_verbosity(LogVerbosity::Error); +pub fn verbosity_no_filter() { + let log_path = &format!("{}/{}", *LOG_DIR, Uuid::new_v4()); + println!("Log Path: {log_path}"); + let messages = vec![ + LogMessageType::GenericErr(String::from("Test Err")).log_message(), + LogMessageType::GenericWarn(String::from("Test Warn")).log_message(), + LogMessageType::GenericInfo(String::from("Test Info")).log_message(), + LogMessageType::GenericDebug(String::from("Test Debug")).log_message(), + ]; + let mut config = config::DEFAULTS; + config.set_log_path(PathBuf::from(log_path)); + config.set_log_verbosity(LogVerbosity::Error); - create_dir_all(PathBuf::from(LOG_DIR.as_str())) - .unwrap_or_else(|_| panic!("Could not create directory: {}", *LOG_DIR)); + create_dir_all(PathBuf::from(LOG_DIR.as_str())) + .unwrap_or_else(|_| panic!("Could not create directory: {}", *LOG_DIR)); - let log_line = log_to_str(&messages[0], &config, file!(), line!(), column!()).expect("There should be a log line.") + "\n"; + let log_line = log_to_str(&messages[0], &config, file!(), line!(), column!()) + .expect("There should be a log line.") + + "\n"; - for msg in messages - { - log_message(&msg, &config, file!(), line!(), column!()); - } + for msg in messages { + log_message(&msg, &config, file!(), line!(), column!()); + } - let log_file = read_to_string(PathBuf::from(log_path)) - .unwrap_or_else(|_| panic!("Could not read file: {log_path}")); + let log_file = read_to_string(PathBuf::from(log_path)) + .unwrap_or_else(|_| panic!("Could not read file: {log_path}")); - assert_eq!(log_file, log_line); + assert_eq!(log_file, log_line); } /// Tests if messages are properly filtered according to their verbosity. #[test] -pub fn verbosity_filter() -{ - let log_path = &format!("{}/{}", *LOG_DIR, Uuid::new_v4()); - println!("Log Path: {log_path}"); - let messages = vec![ - LogMessageType::GenericErr(String::from("Test Err")).log_message(), - LogMessageType::GenericWarn(String::from("Test Warn")).log_message(), - LogMessageType::GenericInfo(String::from("Test Info")).log_message(), - LogMessageType::GenericDebug(String::from("Test Debug")).log_message(), - ]; - let mut config = config::DEFAULTS; - config.set_log_path(PathBuf::from(log_path)); +pub fn verbosity_filter() { + let log_path = &format!("{}/{}", *LOG_DIR, Uuid::new_v4()); + println!("Log Path: {log_path}"); + let messages = vec![ + LogMessageType::GenericErr(String::from("Test Err")).log_message(), + LogMessageType::GenericWarn(String::from("Test Warn")).log_message(), + LogMessageType::GenericInfo(String::from("Test Info")).log_message(), + LogMessageType::GenericDebug(String::from("Test Debug")).log_message(), + ]; + let mut config = config::DEFAULTS; + config.set_log_path(PathBuf::from(log_path)); - create_dir_all(PathBuf::from(LOG_DIR.as_str())) - .unwrap_or_else(|_| panic!("Could not create directory: {}", *LOG_DIR)); + create_dir_all(PathBuf::from(LOG_DIR.as_str())) + .unwrap_or_else(|_| panic!("Could not create directory: {}", *LOG_DIR)); - let mut log_line = log_to_str(&messages[0], &config, file!(), line!(), column!()).expect("There should be a log line.") + "\n"; - log_line += &(log_to_str(&messages[1], &config, file!(), line!(), column!()).expect("There should be a log line.") + "\n"); + let mut log_line = log_to_str(&messages[0], &config, file!(), line!(), column!()) + .expect("There should be a log line.") + + "\n"; + log_line += &(log_to_str(&messages[1], &config, file!(), line!(), column!()) + .expect("There should be a log line.") + + "\n"); - for msg in messages - { - log_message(&msg, &config, file!(), line!(), column!()); - } + for msg in messages { + log_message(&msg, &config, file!(), line!(), column!()); + } - let log_file = read_to_string(PathBuf::from(log_path)) - .unwrap_or_else(|_| panic!("Could not read file: {log_path}")); + let log_file = read_to_string(PathBuf::from(log_path)) + .unwrap_or_else(|_| panic!("Could not read file: {log_path}")); - assert_eq!(log_file, log_line); + assert_eq!(log_file, log_line); } - /** * All testing concurrency in a controlled manner. * @@ -145,118 +152,124 @@ pub fn verbosity_filter() * it might read the result from the changed config. */ #[test] -pub fn concurrency_tests() -{ - log_macro_file(); - log_concurrently_any_order(); - log_concurrently_correct_order(); +pub fn concurrency_tests() { + log_macro_file(); + log_concurrently_any_order(); + log_concurrently_correct_order(); } /** * Tests if log macro logs to file correctly. * [`config::CONFIG`] */ -fn log_macro_file() -{ - let log_path = &format!("{}/{}", *LOG_DIR, Uuid::new_v4()); - println!("Log Path: {log_path}"); - let message = LogMessageType::GenericWarn(String::from("Test Log")).log_message(); - let mut config = config::CONFIG.write().expect("Could not acquire write lock on config!"); - take(&mut *config); - config.set_log_path(PathBuf::from(log_path)); - drop(config); +fn log_macro_file() { + let log_path = &format!("{}/{}", *LOG_DIR, Uuid::new_v4()); + println!("Log Path: {log_path}"); + let message = LogMessageType::GenericWarn(String::from("Test Log")).log_message(); + let mut config = config::CONFIG + .write() + .expect("Could not acquire write lock on config!"); + take(&mut *config); + config.set_log_path(PathBuf::from(log_path)); + drop(config); - create_dir_all(PathBuf::from(LOG_DIR.as_str())) - .unwrap_or_else(|_| panic!("Could not create directory: {}", *LOG_DIR)); + create_dir_all(PathBuf::from(LOG_DIR.as_str())) + .unwrap_or_else(|_| panic!("Could not create directory: {}", *LOG_DIR)); - log!(&message); + log!(&message); - let log_file = read_to_string(PathBuf::from(log_path)) - .unwrap_or_else(|_| panic!("Could not read file: {log_path}")); + let log_file = read_to_string(PathBuf::from(log_path)) + .unwrap_or_else(|_| panic!("Could not read file: {log_path}")); - assert_eq!(message.to_string()+"\n", log_file); + assert_eq!(message.to_string() + "\n", log_file); } /** * Tests concurrent logging. Log lines may be in any order. */ -fn log_concurrently_any_order() -{ - let log_path = &format!("{}/{}", *LOG_DIR, Uuid::new_v4()); - println!("Log Path: {log_path}"); - let mut config = config::CONFIG.write().expect("Could not acquire write lock on config!"); - take(&mut *config); - let mut messages = Vec::with_capacity(CONCURRENT_MESSAGE_COUNT); - config.set_log_path(PathBuf::from(log_path)); - drop(config); +fn log_concurrently_any_order() { + let log_path = &format!("{}/{}", *LOG_DIR, Uuid::new_v4()); + println!("Log Path: {log_path}"); + let mut config = config::CONFIG + .write() + .expect("Could not acquire write lock on config!"); + take(&mut *config); + let mut messages = Vec::with_capacity(CONCURRENT_MESSAGE_COUNT); + config.set_log_path(PathBuf::from(log_path)); + drop(config); - for i in 0..CONCURRENT_MESSAGE_COUNT - { - let msg = i.to_string(); - messages.push( - async { - log!(&LogMessageType::GenericWarn(msg).log_message()); - } - ); - } + for i in 0..CONCURRENT_MESSAGE_COUNT { + let msg = i.to_string(); + messages.push(async { + log!(&LogMessageType::GenericWarn(msg).log_message()); + }); + } - block_on(join_all(messages)); + block_on(join_all(messages)); - let mut num_set = HashSet::with_capacity(CONCURRENT_MESSAGE_COUNT); - for i in 0..CONCURRENT_MESSAGE_COUNT - { - num_set.insert(i); - } + let mut num_set = HashSet::with_capacity(CONCURRENT_MESSAGE_COUNT); + for i in 0..CONCURRENT_MESSAGE_COUNT { + num_set.insert(i); + } - for line in read_to_string(PathBuf::from(log_path)).unwrap_or_else(|_| panic!("Could not read file: {log_path}")).lines() - { - let num_str = line.split_whitespace().last().unwrap_or_else(|| panic!("Could not get message number from line: {line}")); - let num = usize::from_str_radix(num_str, 10).unwrap_or_else(|_| panic!("Could not parse number: {num_str}")); - assert!(num_set.remove(&num)); - } + for line in read_to_string(PathBuf::from(log_path)) + .unwrap_or_else(|_| panic!("Could not read file: {log_path}")) + .lines() + { + let num_str = line + .split_whitespace() + .last() + .unwrap_or_else(|| panic!("Could not get message number from line: {line}")); + let num = usize::from_str_radix(num_str, 10) + .unwrap_or_else(|_| panic!("Could not parse number: {num_str}")); + assert!(num_set.remove(&num)); + } - assert_eq!(num_set.len(), 0); + assert_eq!(num_set.len(), 0); } /** * Tests concurrent logging. Log lines must be in order. */ -fn log_concurrently_correct_order() -{ - let log_path = &format!("{}/{}", *LOG_DIR, Uuid::new_v4()); - println!("Log Path: {log_path}"); - let mut config = config::CONFIG.write().expect("Could not acquire write lock on config!"); - take(&mut *config); - let mut messages = Vec::with_capacity(CONCURRENT_MESSAGE_COUNT); - config.set_log_path(PathBuf::from(log_path)); - drop(config); +fn log_concurrently_correct_order() { + let log_path = &format!("{}/{}", *LOG_DIR, Uuid::new_v4()); + println!("Log Path: {log_path}"); + let mut config = config::CONFIG + .write() + .expect("Could not acquire write lock on config!"); + take(&mut *config); + let mut messages = Vec::with_capacity(CONCURRENT_MESSAGE_COUNT); + config.set_log_path(PathBuf::from(log_path)); + drop(config); - for i in 0..CONCURRENT_MESSAGE_COUNT - { - let msg = i.to_string(); - messages.push( - async { - log!(&LogMessageType::GenericWarn(msg).log_message()); - } - ); - } + for i in 0..CONCURRENT_MESSAGE_COUNT { + let msg = i.to_string(); + messages.push(async { + log!(&LogMessageType::GenericWarn(msg).log_message()); + }); + } - block_on(join_all(messages)); + block_on(join_all(messages)); - let mut num_set = HashSet::with_capacity(CONCURRENT_MESSAGE_COUNT); - for i in 0..CONCURRENT_MESSAGE_COUNT - { - num_set.insert(i); - } + let mut num_set = HashSet::with_capacity(CONCURRENT_MESSAGE_COUNT); + for i in 0..CONCURRENT_MESSAGE_COUNT { + num_set.insert(i); + } - for (i, line) in read_to_string(PathBuf::from(log_path)).unwrap_or_else(|_| panic!("Could not read file: {log_path}")).lines().enumerate() - { - let num_str = line.split_whitespace().last().unwrap_or_else(|| panic!("Could not get message number from line: {line}")); - let num = usize::from_str_radix(num_str, 10).unwrap_or_else(|_| panic!("Could not parse number: {num_str}")); - assert!(num_set.remove(&num)); - assert_eq!(i, num); - } - - assert_eq!(num_set.len(), 0); + for (i, line) in read_to_string(PathBuf::from(log_path)) + .unwrap_or_else(|_| panic!("Could not read file: {log_path}")) + .lines() + .enumerate() + { + let num_str = line + .split_whitespace() + .last() + .unwrap_or_else(|| panic!("Could not get message number from line: {line}")); + let num = usize::from_str_radix(num_str, 10) + .unwrap_or_else(|_| panic!("Could not parse number: {num_str}")); + assert!(num_set.remove(&num)); + assert_eq!(i, num); + } + assert_eq!(num_set.len(), 0); } -- 2.45.2 From eed7053b6d6f56af7999f041b5b295f61335b423 Mon Sep 17 00:00:00 2001 From: Leon Wilzer Date: Thu, 15 Feb 2024 02:49:56 +0100 Subject: [PATCH 14/27] replaced from_str_radix with parse --- logging/src/lib.rs | 4 +++- logging/src/test.rs | 6 ++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/logging/src/lib.rs b/logging/src/lib.rs index 903f2ed..c187c95 100644 --- a/logging/src/lib.rs +++ b/logging/src/lib.rs @@ -30,7 +30,9 @@ use LogMessageType::GenericWarn; * or if another error occurs, such as a full disk. */ pub fn log_message(msg: &LogMessage, conf: &Config, file: &str, line: u32, column: u32) { - let Some(log_line) = log_to_str(msg, conf, file, line, column) else { return }; + let Some(log_line) = log_to_str(msg, conf, file, line, column) else { + return; + }; // May panic if file cannot be opened or written to. conf.log_path().as_ref().map_or_else( diff --git a/logging/src/test.rs b/logging/src/test.rs index 18ff52b..cb351a0 100644 --- a/logging/src/test.rs +++ b/logging/src/test.rs @@ -220,7 +220,8 @@ fn log_concurrently_any_order() { .split_whitespace() .last() .unwrap_or_else(|| panic!("Could not get message number from line: {line}")); - let num = usize::from_str_radix(num_str, 10) + let num = num_str + .parse() .unwrap_or_else(|_| panic!("Could not parse number: {num_str}")); assert!(num_set.remove(&num)); } @@ -265,7 +266,8 @@ fn log_concurrently_correct_order() { .split_whitespace() .last() .unwrap_or_else(|| panic!("Could not get message number from line: {line}")); - let num = usize::from_str_radix(num_str, 10) + let num = num_str + .parse() .unwrap_or_else(|_| panic!("Could not parse number: {num_str}")); assert!(num_set.remove(&num)); assert_eq!(i, num); -- 2.45.2 From 6e73c2a7b7791c8ef92c7f0b9102902d51e8baec Mon Sep 17 00:00:00 2001 From: Leon Wilzer Date: Thu, 15 Feb 2024 18:41:33 +0100 Subject: [PATCH 15/27] added explicit ordinals; See #26 --- config/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/config/src/lib.rs b/config/src/lib.rs index e62ac8a..ada1351 100644 --- a/config/src/lib.rs +++ b/config/src/lib.rs @@ -140,15 +140,15 @@ pub static CONFIG: RwLock = RwLock::new(DEFAULTS); #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum LogVerbosity { /// Critical Errors, may lead to crashes or deactivating certain features. - Error, + Error = 10, /// Very minor and recovered errors, such as invalid configs. - Warning, + Warning = 20, /// Very verbose and detailed. Basically gives a step-by-step instruction on what is currently done. - Information, + Information = 30, /// Very technical and even more verbose. /// May contain secrets and private information. /// **Do not use in production environments!** - Debugging, + Debugging = 40, } impl PartialOrd for LogVerbosity { -- 2.45.2 From a36a8a8b76e015d744d3fdad835b06fad0fb3c03 Mon Sep 17 00:00:00 2001 From: Leon Wilzer Date: Thu, 15 Feb 2024 19:04:44 +0100 Subject: [PATCH 16/27] expanded macro to optionally accept &Config This is necessary, in case there is a write lock in the current scope, which would prevent the log command from exiting --- logging/src/lib.rs | 3 +++ logging/src/test.rs | 25 +++++++++++++++++++++++-- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/logging/src/lib.rs b/logging/src/lib.rs index c187c95..37eb316 100644 --- a/logging/src/lib.rs +++ b/logging/src/lib.rs @@ -103,6 +103,9 @@ macro_rules! log { log_message($msg, &*conf, file!(), line!(), column!()); drop(conf); }; + ($msg:expr, $config:expr) => { + log_message($msg, $config, file!(), line!(), column!()); + }; } /** diff --git a/logging/src/test.rs b/logging/src/test.rs index cb351a0..458a5a2 100644 --- a/logging/src/test.rs +++ b/logging/src/test.rs @@ -35,6 +35,27 @@ static LOG_DIR: Lazy = Lazy::new(|| { } }); +/// Tests if the macro logs properly with a given config. +#[test] +pub fn log_macro_given_config() +{ + let log_path = &format!("{}/{}", *LOG_DIR, Uuid::new_v4()); + println!("Log Path: {log_path}"); + let message = LogMessageType::GenericWarn(String::from("Test Log")).log_message(); + let mut config = config::DEFAULTS; + config.set_log_path(PathBuf::from(log_path)); + + create_dir_all(PathBuf::from(LOG_DIR.as_str())) + .unwrap_or_else(|_| panic!("Could not create directory: {}", *LOG_DIR)); + + log!(&message, &config); + + let log_file = read_to_string(PathBuf::from(log_path)) + .unwrap_or_else(|_| panic!("Could not read file: {log_path}")); + + assert_eq!(message.to_string() + "\n", log_file); +} + /// Tests if [`log_message`] to file correctly. #[test] pub fn log_msg_file() { @@ -153,7 +174,7 @@ pub fn verbosity_filter() { */ #[test] pub fn concurrency_tests() { - log_macro_file(); + log_macro_shared_config(); log_concurrently_any_order(); log_concurrently_correct_order(); } @@ -162,7 +183,7 @@ pub fn concurrency_tests() { * Tests if log macro logs to file correctly. * [`config::CONFIG`] */ -fn log_macro_file() { +fn log_macro_shared_config() { let log_path = &format!("{}/{}", *LOG_DIR, Uuid::new_v4()); println!("Log Path: {log_path}"); let message = LogMessageType::GenericWarn(String::from("Test Log")).log_message(); -- 2.45.2 From 9e378c78204a597f7cd0c9c371fd4980209b79db Mon Sep 17 00:00:00 2001 From: Leon Wilzer Date: Thu, 15 Feb 2024 18:07:40 +0000 Subject: [PATCH 17/27] rustfmt --- logging/src/lib.rs | 4 ++-- logging/src/test.rs | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/logging/src/lib.rs b/logging/src/lib.rs index 37eb316..e436fea 100644 --- a/logging/src/lib.rs +++ b/logging/src/lib.rs @@ -103,9 +103,9 @@ macro_rules! log { log_message($msg, &*conf, file!(), line!(), column!()); drop(conf); }; - ($msg:expr, $config:expr) => { + ($msg:expr, $config:expr) => { log_message($msg, $config, file!(), line!(), column!()); - }; + }; } /** diff --git a/logging/src/test.rs b/logging/src/test.rs index 458a5a2..2045bf9 100644 --- a/logging/src/test.rs +++ b/logging/src/test.rs @@ -37,8 +37,7 @@ static LOG_DIR: Lazy = Lazy::new(|| { /// Tests if the macro logs properly with a given config. #[test] -pub fn log_macro_given_config() -{ +pub fn log_macro_given_config() { let log_path = &format!("{}/{}", *LOG_DIR, Uuid::new_v4()); println!("Log Path: {log_path}"); let message = LogMessageType::GenericWarn(String::from("Test Log")).log_message(); -- 2.45.2 From a787dd93e58398134a1f8860372687a390dc85ac Mon Sep 17 00:00:00 2001 From: Leon Wilzer Date: Thu, 15 Feb 2024 21:38:15 +0100 Subject: [PATCH 18/27] Split config crate into multiple modules this was done to have more fine-grain control over mutability over the different configs and settings of WANessa --- config/Cargo.toml | 1 + config/src/db.rs | 65 ++++++++++++++++++ config/src/lib.rs | 171 ++++------------------------------------------ config/src/log.rs | 89 ++++++++++++++++++++++++ 4 files changed, 167 insertions(+), 159 deletions(-) create mode 100644 config/src/db.rs create mode 100644 config/src/log.rs diff --git a/config/Cargo.toml b/config/Cargo.toml index d915604..4e5c462 100644 --- a/config/Cargo.toml +++ b/config/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" [dependencies] getset = { version = "0.1.2", default-features = false } +once_cell = { version = "1.19.0", default-features = false, features = ["std"] } [lints.rust] unsafe_code = "forbid" diff --git a/config/src/db.rs b/config/src/db.rs new file mode 100644 index 0000000..7ba05e8 --- /dev/null +++ b/config/src/db.rs @@ -0,0 +1,65 @@ +/*! +* This module contains everything related to [`DbConfig`]. +*/ +#![allow(clippy::module_name_repetitions)] + +use getset::Getters; + + +/** +* A immutable record of all the information needed to connect to the SQL database. +*/ +#[allow(clippy::module_name_repetitions)] +#[derive(Clone, PartialEq, Eq, Getters, Debug)] +#[getset(get = "pub")] +pub struct DbConfig +{ + /// Database connection address. + /// Is an option to allow constructing a default config during compile time.
+ addr: String, + /// Database connection port.
+ port: u16, +} + +impl Default for DbConfig +{ + fn default() -> Self + { + Self { + addr: String::from("localhost"), + port: 6969 + } + } +} + +/** +* Builder for [`DbConfig`]. +*/ +#[derive(Default, Debug)] +pub struct DbConfigBuilder(DbConfig); + +impl DbConfigBuilder +{ + /// Get a new [`DbConfigBuilder`] + #[must_use] + pub fn new() -> Self + { + Self::default() + } + + /// Set the address to the location of the database. + #[must_use] + pub fn set_addr(mut self, addr: impl Into) -> Self + { + self.0.addr = addr.into(); + self + } + + /// Set the port to the port the database uses. + #[must_use] + pub fn set_port(mut self, port: impl Into) -> Self + { + self.0.port = port.into(); + self + } +} diff --git a/config/src/lib.rs b/config/src/lib.rs index ada1351..3f10a4d 100644 --- a/config/src/lib.rs +++ b/config/src/lib.rs @@ -1,168 +1,21 @@ /*! -* A singleton, thread-safe struct used for accessing +* Containing all singleton and thread-safe structs related to configuring `WANessa`. */ -use std::path::Path; -use std::path::PathBuf; -use std::sync::RwLock; +pub mod log; +pub mod db; -use std::cmp::Ordering; +use crate::db::DbConfig; +use crate::log::LogSettings; -use crate::LogVerbosity::Warning; +use std::sync::{Arc, RwLock}; -use getset::Getters; -use getset::Setters; +use once_cell::sync::Lazy; -/** -* Represents a valid `WANessa` configuration. Intended as a read-only singleton. -* See [`DEFAULTS`] -*/ -#[derive(Clone, PartialEq, Eq, Getters, Setters, Debug)] -#[getset(get = "pub", set = "pub")] -#[allow(clippy::struct_excessive_bools)] // False positive, since this is a config struct. -pub struct Config { - /// See [`LogVerbosity`].
- /// Default: [`Warning`] - log_verbosity: LogVerbosity, - /// Logs UTC time and date of message, if true.
- /// Default: `false` - log_time: bool, - /// Time and date format.
- /// Defaults to `%F/%T:%f`.
- /// See [chrono](https://docs.rs/chrono/latest/chrono/format/strftime/index.html).
- /// Default example : `2001-12-31/23:59:33:26490000` - #[getset(skip)] - log_time_format: Option, - /// Logs location in code, where the message was logged, if true.
- /// Default: `false` - log_location: bool, - /// If `Some(path)` tries to also write the log to `path` in addition to stderr/stderr.
- /// Default: [None] - #[getset(skip)] - log_path: Option, - /// Logs to standard out, if true.
- /// Default: `true` - log_stdout: bool, - /// Logs to standard err, if true.
- /// Default: `true` - log_stderr: bool, - /// Database connection address. - /// Is an option to allow constructing a default config during compile time.
- /// Default: `localhost`. - #[getset(skip)] - db_addr: Option, - /// Database connection port.
- /// Default: `6969` (nice). - db_port: u16, -} +// TODO: replace default with parsed settings from files+flags -impl Config { - /// Getter for [`Self::db_addr`]. - #[must_use] - pub fn db_addr(&self) -> &str { - self.db_addr.as_ref().map_or("localhost", |addr| addr) - } +/// Singelton [`DbConfig`]. +pub static DB_CONFIG: Lazy> = Lazy::new(|| Arc::new(DbConfig::default())); - /// Setter for [`Self::db_addr`]. - pub fn set_db_addr(&mut self, db_addr: impl Into) { - self.db_addr = Some(db_addr.into()); - } - - /// Getter for [`Self::log_time_format`]. - #[must_use] - pub fn log_time_format(&self) -> &str { - self.log_time_format.as_ref().map_or("%F-%T:%f", |fmt| fmt) - } - - /// Setter for [`Self::log_time_format`]. - pub fn set_log_time_format(&mut self, format: impl Into) { - self.log_time_format = Some(format.into()); - } - - /// Getter for [`Self::log_path`]. - #[must_use] - pub fn log_path(&self) -> Option<&Path> { - self.log_path.as_deref() - } - - /// Setter for [`Self::log_path`]. - pub fn set_log_path(&mut self, log_path: impl Into>) { - self.log_path = log_path.into(); - } -} - -/// See [`DEFAULTS`]. -impl Default for Config { - /// See [`DEFAULTS`]. - fn default() -> Self { - DEFAULTS - } -} - -/** -* Default configuration. -* ```rust -* # use config::Config; -* # use config::LogVerbosity::Warning; -* let mut defaults = Config::default(); -* -* defaults.set_log_verbosity(Warning); -* defaults.set_log_time(false); -* assert_eq!(defaults.log_time_format(), "%F-%T:%f"); -* defaults.set_log_location(false); -* defaults.set_log_stdout(true); -* defaults.set_log_stderr(true); -* defaults.set_log_path(None); -* assert_eq!(defaults.db_addr(), "localhost"); -* defaults.set_db_port(6969); -* -* # assert_eq!(defaults,config::DEFAULTS) -* ``` -*/ -pub const DEFAULTS: Config = Config { - log_verbosity: Warning, - log_time: false, - log_time_format: None, - log_location: false, - log_stdout: true, - log_stderr: true, - log_path: None, - db_addr: None, - db_port: 6969, -}; - -/// Configuration singleton. -pub static CONFIG: RwLock = RwLock::new(DEFAULTS); - -/** -* Each level includes the previous ones. -*/ -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum LogVerbosity { - /// Critical Errors, may lead to crashes or deactivating certain features. - Error = 10, - /// Very minor and recovered errors, such as invalid configs. - Warning = 20, - /// Very verbose and detailed. Basically gives a step-by-step instruction on what is currently done. - Information = 30, - /// Very technical and even more verbose. - /// May contain secrets and private information. - /// **Do not use in production environments!** - Debugging = 40, -} - -impl PartialOrd for LogVerbosity { - /// Some operator overloading of comparison symbols (==, <,>=, etc.) as syntactic sugar. - /// See [`PartialOrd`]. - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for LogVerbosity { - /// Some operator overloading of comparison symbols (==, <,>=, etc.) as syntactic sugar. - /// See [`Ord`]. - fn cmp(&self, other: &Self) -> Ordering { - (*self as usize).cmp(&(*other as usize)) - } -} +/// Singelton [`LogSettings`]. +pub static LOG_SETTINGS: Lazy> = Lazy::new(|| RwLock::new(LogSettings::default())); diff --git a/config/src/log.rs b/config/src/log.rs new file mode 100644 index 0000000..4685421 --- /dev/null +++ b/config/src/log.rs @@ -0,0 +1,89 @@ +/*! +* This module contains everything related to [`LogSettings`]. +*/ +#![allow(clippy::module_name_repetitions)] +use std::cmp::Ordering; + +use crate::log::LogVerbosity::Warning; + +use std::path::PathBuf; + +use getset::Getters; +use getset::Setters; + + + +/** +* All settings relating to how the project logs information. +*/ +#[allow(clippy::struct_excessive_bools)] +#[derive(Clone, PartialEq, Eq, Getters, Setters, Debug)] +#[getset(get = "pub", set = "pub")] +pub struct LogSettings +{ + /// See [`LogVerbosity`].
+ verbosity: LogVerbosity, + /// Logs UTC time and date of message, if true.
+ time: bool, + /// Time and date format.
+ /// See [chrono](https://docs.rs/chrono/latest/chrono/format/strftime/index.html).
+ time_format: String, + /// Logs location in code, where the message was logged, if true.
+ location: bool, + /// If `Some(path)` tries to also write the log to `path` in addition to stderr/stderr.
+ path: Option, + /// Logs to standard out, if true.
+ stdout: bool, + /// Logs to standard err, if true.
+ stderr: bool, +} + +impl Default for LogSettings +{ + fn default() -> Self + { + Self { + verbosity: Warning, + time: false, + time_format: String::from("%F-%T:%f"), + location: false, + stdout: true, + stderr: true, + path: None, + } + } +} + +/** +* Each level includes the previous ones. +*/ +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum LogVerbosity { + /// Critical Errors, may lead to crashes or deactivating certain features. + Error = 10, + /// Very minor and recovered errors, such as invalid configs. + Warning = 20, + /// Very verbose and detailed. Basically gives a step-by-step instruction on what is currently done. + Information = 30, + /// Very technical and even more verbose. + /// May contain secrets and private information. + /// **Do not use in production environments!** + Debugging = 40, +} + +impl PartialOrd for LogVerbosity { + /// Some operator overloading of comparison symbols (==, <,>=, etc.) as syntactic sugar. + /// See [`PartialOrd`]. + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for LogVerbosity { + /// Some operator overloading of comparison symbols (==, <,>=, etc.) as syntactic sugar. + /// See [`Ord`]. + fn cmp(&self, other: &Self) -> Ordering { + (*self as usize).cmp(&(*other as usize)) + } +} + -- 2.45.2 From cb024af91ac95fda073f780c18a9d4868efe7563 Mon Sep 17 00:00:00 2001 From: Leon Wilzer Date: Thu, 15 Feb 2024 21:53:40 +0100 Subject: [PATCH 19/27] some syntactic sugar for Option enum --- config/src/log.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/config/src/log.rs b/config/src/log.rs index 4685421..18c787f 100644 --- a/config/src/log.rs +++ b/config/src/log.rs @@ -3,6 +3,7 @@ */ #![allow(clippy::module_name_repetitions)] use std::cmp::Ordering; +use std::path::Path; use crate::log::LogVerbosity::Warning; @@ -31,6 +32,7 @@ pub struct LogSettings /// Logs location in code, where the message was logged, if true.
location: bool, /// If `Some(path)` tries to also write the log to `path` in addition to stderr/stderr.
+ #[getset(skip)] path: Option, /// Logs to standard out, if true.
stdout: bool, @@ -38,6 +40,22 @@ pub struct LogSettings stderr: bool, } +impl LogSettings +{ + /// Setter for log path, including syntactic sugar for the [Option] enum. + pub fn set_path(&mut self, path: impl Into>) + { + self.path = path.into(); + } + + /// Getter for the log path. + #[must_use] + pub fn path(&self) -> Option<&Path> + { + self.path.as_deref() + } +} + impl Default for LogSettings { fn default() -> Self -- 2.45.2 From ba0a1fc6411c8f63d38aef7ae9ea8a71a71b5f9a Mon Sep 17 00:00:00 2001 From: Leon Wilzer Date: Thu, 15 Feb 2024 21:54:27 +0100 Subject: [PATCH 20/27] migrated to reworked config crate --- logging/src/lib.rs | 26 +++++++++++++------------- logging/src/test.rs | 34 +++++++++++++++++----------------- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/logging/src/lib.rs b/logging/src/lib.rs index 37eb316..203e74a 100644 --- a/logging/src/lib.rs +++ b/logging/src/lib.rs @@ -9,8 +9,8 @@ use std::fmt::Display; use std::fs::OpenOptions; use std::io::Write; -use config::Config; -use config::LogVerbosity; +use config::log::LogSettings; +use config::log::LogVerbosity; use chrono::Utc; @@ -29,13 +29,13 @@ use LogMessageType::GenericWarn; * Panics if readlock on [`config::CONFIG`] could not be acquired * or if another error occurs, such as a full disk. */ -pub fn log_message(msg: &LogMessage, conf: &Config, file: &str, line: u32, column: u32) { +pub fn log_message(msg: &LogMessage, conf: &LogSettings, file: &str, line: u32, column: u32) { let Some(log_line) = log_to_str(msg, conf, file, line, column) else { return; }; // May panic if file cannot be opened or written to. - conf.log_path().as_ref().map_or_else( + conf.path().as_ref().map_or_else( || {}, |path| { let mut file = OpenOptions::new() @@ -49,17 +49,17 @@ pub fn log_message(msg: &LogMessage, conf: &Config, file: &str, line: u32, colum }, ); - if msg.1 <= Warning && *conf.log_stderr() { + if msg.1 <= Warning && *conf.stderr() { // May panic if writing to stderr fails. eprintln!("{log_line}"); - } else if msg.1 >= Information && *conf.log_stdout() { + } else if msg.1 >= Information && *conf.stdout() { // May panic if writing to stdout fails. println!("{log_line}"); } } /** -* Return log line, if message may be logged according to [`config::Config`]. +* Return log line, if message may be logged according to [`config::log::LogSettings`]. * # Panics * Panics if readlock on [`config::CONFIG`] could not be acquired * or if another error occurs, such as a full disk. @@ -67,24 +67,24 @@ pub fn log_message(msg: &LogMessage, conf: &Config, file: &str, line: u32, colum #[must_use] pub fn log_to_str( msg: &LogMessage, - conf: &Config, + conf: &LogSettings, file: &str, line: u32, column: u32, ) -> Option { - if conf.log_verbosity() < &msg.1 { + if conf.verbosity() < &msg.1 { return None; } let mut log_line = String::new(); // add time substring - if *conf.log_time() { - log_line += &format!("{} ", Utc::now().format(conf.log_time_format())); + if *conf.time() { + log_line += &format!("{} ", Utc::now().format(conf.time_format())); } // add code location substring - if *conf.log_location() { + if *conf.location() { log_line += &format!("{file}:{line},{column} "); } @@ -97,7 +97,7 @@ pub fn log_to_str( #[macro_export] macro_rules! log { ($msg:expr) => { - let conf = config::CONFIG + let conf = config::LOG_SETTINGS .read() .unwrap_or_else(|_| panic!("Failed aqcuire read lock on config!")); log_message($msg, &*conf, file!(), line!(), column!()); diff --git a/logging/src/test.rs b/logging/src/test.rs index 458a5a2..77a49ef 100644 --- a/logging/src/test.rs +++ b/logging/src/test.rs @@ -42,8 +42,8 @@ pub fn log_macro_given_config() let log_path = &format!("{}/{}", *LOG_DIR, Uuid::new_v4()); println!("Log Path: {log_path}"); let message = LogMessageType::GenericWarn(String::from("Test Log")).log_message(); - let mut config = config::DEFAULTS; - config.set_log_path(PathBuf::from(log_path)); + let mut config = config::log::LogSettings::default(); + config.set_path(PathBuf::from(log_path)); create_dir_all(PathBuf::from(LOG_DIR.as_str())) .unwrap_or_else(|_| panic!("Could not create directory: {}", *LOG_DIR)); @@ -62,8 +62,8 @@ pub fn log_msg_file() { let log_path = &format!("{}/{}", *LOG_DIR, Uuid::new_v4()); println!("Log Path: {log_path}"); let message = LogMessageType::GenericWarn(String::from("Test Log")).log_message(); - let mut config = config::DEFAULTS; - config.set_log_path(PathBuf::from(log_path)); + let mut config = config::log::LogSettings::default(); + config.set_path(PathBuf::from(log_path)); create_dir_all(PathBuf::from(LOG_DIR.as_str())) .unwrap_or_else(|_| panic!("Could not create directory: {}", *LOG_DIR)); @@ -82,8 +82,8 @@ pub fn log_str() { let log_path = &format!("{}/{}", *LOG_DIR, Uuid::new_v4()); println!("Log Path: {log_path}"); let message = LogMessageType::GenericWarn(String::from("Test Log")).log_message(); - let mut config = config::DEFAULTS; - config.set_log_path(PathBuf::from(log_path)); + let mut config = config::log::LogSettings::default(); + config.set_path(PathBuf::from(log_path)); create_dir_all(PathBuf::from(LOG_DIR.as_str())) .unwrap_or_else(|_| panic!("Could not create directory: {}", *LOG_DIR)); @@ -111,9 +111,9 @@ pub fn verbosity_no_filter() { LogMessageType::GenericInfo(String::from("Test Info")).log_message(), LogMessageType::GenericDebug(String::from("Test Debug")).log_message(), ]; - let mut config = config::DEFAULTS; - config.set_log_path(PathBuf::from(log_path)); - config.set_log_verbosity(LogVerbosity::Error); + let mut config = config::log::LogSettings::default(); + config.set_path(PathBuf::from(log_path)); + config.set_verbosity(LogVerbosity::Error); create_dir_all(PathBuf::from(LOG_DIR.as_str())) .unwrap_or_else(|_| panic!("Could not create directory: {}", *LOG_DIR)); @@ -143,8 +143,8 @@ pub fn verbosity_filter() { LogMessageType::GenericInfo(String::from("Test Info")).log_message(), LogMessageType::GenericDebug(String::from("Test Debug")).log_message(), ]; - let mut config = config::DEFAULTS; - config.set_log_path(PathBuf::from(log_path)); + let mut config = config::log::LogSettings::default(); + config.set_path(PathBuf::from(log_path)); create_dir_all(PathBuf::from(LOG_DIR.as_str())) .unwrap_or_else(|_| panic!("Could not create directory: {}", *LOG_DIR)); @@ -187,11 +187,11 @@ fn log_macro_shared_config() { let log_path = &format!("{}/{}", *LOG_DIR, Uuid::new_v4()); println!("Log Path: {log_path}"); let message = LogMessageType::GenericWarn(String::from("Test Log")).log_message(); - let mut config = config::CONFIG + let mut config = config::LOG_SETTINGS .write() .expect("Could not acquire write lock on config!"); take(&mut *config); - config.set_log_path(PathBuf::from(log_path)); + config.set_path(PathBuf::from(log_path)); drop(config); create_dir_all(PathBuf::from(LOG_DIR.as_str())) @@ -211,12 +211,12 @@ fn log_macro_shared_config() { fn log_concurrently_any_order() { let log_path = &format!("{}/{}", *LOG_DIR, Uuid::new_v4()); println!("Log Path: {log_path}"); - let mut config = config::CONFIG + let mut config = config::LOG_SETTINGS .write() .expect("Could not acquire write lock on config!"); take(&mut *config); let mut messages = Vec::with_capacity(CONCURRENT_MESSAGE_COUNT); - config.set_log_path(PathBuf::from(log_path)); + config.set_path(PathBuf::from(log_path)); drop(config); for i in 0..CONCURRENT_MESSAGE_COUNT { @@ -256,12 +256,12 @@ fn log_concurrently_any_order() { fn log_concurrently_correct_order() { let log_path = &format!("{}/{}", *LOG_DIR, Uuid::new_v4()); println!("Log Path: {log_path}"); - let mut config = config::CONFIG + let mut config = config::LOG_SETTINGS .write() .expect("Could not acquire write lock on config!"); take(&mut *config); let mut messages = Vec::with_capacity(CONCURRENT_MESSAGE_COUNT); - config.set_log_path(PathBuf::from(log_path)); + config.set_path(PathBuf::from(log_path)); drop(config); for i in 0..CONCURRENT_MESSAGE_COUNT { -- 2.45.2 From cbfa4cbfdf100d6246e32f0e979418e0eb2be9f3 Mon Sep 17 00:00:00 2001 From: Leon Wilzer Date: Thu, 15 Feb 2024 20:56:18 +0000 Subject: [PATCH 21/27] rustfmt --- config/src/db.rs | 60 ++++++++++++++++++++--------------------------- config/src/lib.rs | 5 ++-- config/src/log.rs | 57 +++++++++++++++++++------------------------- 3 files changed, 53 insertions(+), 69 deletions(-) diff --git a/config/src/db.rs b/config/src/db.rs index 7ba05e8..ad74bf2 100644 --- a/config/src/db.rs +++ b/config/src/db.rs @@ -5,15 +5,13 @@ use getset::Getters; - /** * A immutable record of all the information needed to connect to the SQL database. */ #[allow(clippy::module_name_repetitions)] #[derive(Clone, PartialEq, Eq, Getters, Debug)] #[getset(get = "pub")] -pub struct DbConfig -{ +pub struct DbConfig { /// Database connection address. /// Is an option to allow constructing a default config during compile time.
addr: String, @@ -21,15 +19,13 @@ pub struct DbConfig port: u16, } -impl Default for DbConfig -{ - fn default() -> Self - { - Self { - addr: String::from("localhost"), - port: 6969 - } - } +impl Default for DbConfig { + fn default() -> Self { + Self { + addr: String::from("localhost"), + port: 6969, + } + } } /** @@ -38,28 +34,24 @@ impl Default for DbConfig #[derive(Default, Debug)] pub struct DbConfigBuilder(DbConfig); -impl DbConfigBuilder -{ - /// Get a new [`DbConfigBuilder`] - #[must_use] - pub fn new() -> Self - { - Self::default() - } +impl DbConfigBuilder { + /// Get a new [`DbConfigBuilder`] + #[must_use] + pub fn new() -> Self { + Self::default() + } - /// Set the address to the location of the database. - #[must_use] - pub fn set_addr(mut self, addr: impl Into) -> Self - { - self.0.addr = addr.into(); - self - } + /// Set the address to the location of the database. + #[must_use] + pub fn set_addr(mut self, addr: impl Into) -> Self { + self.0.addr = addr.into(); + self + } - /// Set the port to the port the database uses. - #[must_use] - pub fn set_port(mut self, port: impl Into) -> Self - { - self.0.port = port.into(); - self - } + /// Set the port to the port the database uses. + #[must_use] + pub fn set_port(mut self, port: impl Into) -> Self { + self.0.port = port.into(); + self + } } diff --git a/config/src/lib.rs b/config/src/lib.rs index 3f10a4d..7dfda86 100644 --- a/config/src/lib.rs +++ b/config/src/lib.rs @@ -2,8 +2,8 @@ * Containing all singleton and thread-safe structs related to configuring `WANessa`. */ -pub mod log; pub mod db; +pub mod log; use crate::db::DbConfig; use crate::log::LogSettings; @@ -18,4 +18,5 @@ use once_cell::sync::Lazy; pub static DB_CONFIG: Lazy> = Lazy::new(|| Arc::new(DbConfig::default())); /// Singelton [`LogSettings`]. -pub static LOG_SETTINGS: Lazy> = Lazy::new(|| RwLock::new(LogSettings::default())); +pub static LOG_SETTINGS: Lazy> = + Lazy::new(|| RwLock::new(LogSettings::default())); diff --git a/config/src/log.rs b/config/src/log.rs index 18c787f..87f2c4e 100644 --- a/config/src/log.rs +++ b/config/src/log.rs @@ -12,16 +12,13 @@ use std::path::PathBuf; use getset::Getters; use getset::Setters; - - /** * All settings relating to how the project logs information. */ #[allow(clippy::struct_excessive_bools)] #[derive(Clone, PartialEq, Eq, Getters, Setters, Debug)] #[getset(get = "pub", set = "pub")] -pub struct LogSettings -{ +pub struct LogSettings { /// See [`LogVerbosity`].
verbosity: LogVerbosity, /// Logs UTC time and date of message, if true.
@@ -32,7 +29,7 @@ pub struct LogSettings /// Logs location in code, where the message was logged, if true.
location: bool, /// If `Some(path)` tries to also write the log to `path` in addition to stderr/stderr.
- #[getset(skip)] + #[getset(skip)] path: Option, /// Logs to standard out, if true.
stdout: bool, @@ -40,36 +37,31 @@ pub struct LogSettings stderr: bool, } -impl LogSettings -{ - /// Setter for log path, including syntactic sugar for the [Option] enum. - pub fn set_path(&mut self, path: impl Into>) - { - self.path = path.into(); - } +impl LogSettings { + /// Setter for log path, including syntactic sugar for the [Option] enum. + pub fn set_path(&mut self, path: impl Into>) { + self.path = path.into(); + } - /// Getter for the log path. - #[must_use] - pub fn path(&self) -> Option<&Path> - { - self.path.as_deref() - } + /// Getter for the log path. + #[must_use] + pub fn path(&self) -> Option<&Path> { + self.path.as_deref() + } } -impl Default for LogSettings -{ - fn default() -> Self - { - Self { - verbosity: Warning, - time: false, - time_format: String::from("%F-%T:%f"), - location: false, - stdout: true, - stderr: true, - path: None, - } - } +impl Default for LogSettings { + fn default() -> Self { + Self { + verbosity: Warning, + time: false, + time_format: String::from("%F-%T:%f"), + location: false, + stdout: true, + stderr: true, + path: None, + } + } } /** @@ -104,4 +96,3 @@ impl Ord for LogVerbosity { (*self as usize).cmp(&(*other as usize)) } } - -- 2.45.2 From 927a8d6d05767fa7e4d4f72d08b6ff7e421c2093 Mon Sep 17 00:00:00 2001 From: Leon Wilzer Date: Thu, 15 Feb 2024 22:40:58 +0100 Subject: [PATCH 22/27] rustdoc adjustments --- config/src/lib.rs | 9 +++++++++ logging/src/lib.rs | 9 ++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/config/src/lib.rs b/config/src/lib.rs index 7dfda86..4534cfe 100644 --- a/config/src/lib.rs +++ b/config/src/lib.rs @@ -1,5 +1,14 @@ /*! * Containing all singleton and thread-safe structs related to configuring `WANessa`. +* +* This crate differentiates between `Configs` and `Settings`: +* Configs: +* - Configs are immutable after they have been initialized. Example [`DbConfig`]. +* - Changing the config requires a restart of `WANessa`. +* +* Settings: +* - Settings are mutable after they have been initialized. Example [`LogSettings`]. +* - `WANessa` always uses the current setting and does not need a restart to apply new settings. */ pub mod db; diff --git a/logging/src/lib.rs b/logging/src/lib.rs index bfcf4e9..596596b 100644 --- a/logging/src/lib.rs +++ b/logging/src/lib.rs @@ -1,6 +1,6 @@ /*! * This module handles logging messages asynchronously and is thread-safe. -* It should mostly be called statically. +* It should mostly be called statically using the [`log!`] macro. */ mod test; @@ -92,7 +92,10 @@ pub fn log_to_str( } /** -* Shorthand version for [`log_message`], which does not require information about the [`config::Config::log_location`]. +* Shorthand version for [`log_message`], which does not require information about where in the code +* the command was called from. +* Tries to aqcuire a read lock, if a reference to an instance of [`config::log::LogSettings`] is +* not given. */ #[macro_export] macro_rules! log { @@ -109,7 +112,7 @@ macro_rules! log { } /** -* A named typle assigning a log [`LogVerbosity`] to a [`LogMessageType`] +* A named typle assigning a log [`LogVerbosity`] to a [`LogMessageType`]. */ #[derive(PartialEq, Eq, Debug)] pub struct LogMessage(LogMessageType, LogVerbosity); -- 2.45.2 From dd5bfff7d2f0859e92ca1ca1b068638e3eb1333c Mon Sep 17 00:00:00 2001 From: Leon Wilzer Date: Thu, 15 Feb 2024 22:41:13 +0100 Subject: [PATCH 23/27] replaced log_message with new log! --- logging/src/test.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/logging/src/test.rs b/logging/src/test.rs index fc614ea..08f8787 100644 --- a/logging/src/test.rs +++ b/logging/src/test.rs @@ -67,7 +67,7 @@ pub fn log_msg_file() { create_dir_all(PathBuf::from(LOG_DIR.as_str())) .unwrap_or_else(|_| panic!("Could not create directory: {}", *LOG_DIR)); - log_message(&message, &config, file!(), line!(), column!()); + log!(&message, &config); let log_file = read_to_string(PathBuf::from(log_path)) .unwrap_or_else(|_| panic!("Could not read file: {log_path}")); @@ -91,7 +91,7 @@ pub fn log_str() { .expect("There should be a log line.") + "\n"; - log_message(&message, &config, file!(), line!(), column!()); + log!(&message, &config); let log_file = read_to_string(PathBuf::from(log_path)) .unwrap_or_else(|_| panic!("Could not read file: {log_path}")); @@ -122,7 +122,7 @@ pub fn verbosity_no_filter() { + "\n"; for msg in messages { - log_message(&msg, &config, file!(), line!(), column!()); + log!(&msg, &config); } let log_file = read_to_string(PathBuf::from(log_path)) @@ -156,7 +156,7 @@ pub fn verbosity_filter() { + "\n"); for msg in messages { - log_message(&msg, &config, file!(), line!(), column!()); + log!(&msg, &config); } let log_file = read_to_string(PathBuf::from(log_path)) -- 2.45.2 From e4baaa5f451f933ce139e17f564c5a96a101bb32 Mon Sep 17 00:00:00 2001 From: Leon Wilzer Date: Mon, 6 May 2024 21:03:09 +0200 Subject: [PATCH 24/27] removed suicidal tendencies --- logging/src/lib.rs | 58 +++++++++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/logging/src/lib.rs b/logging/src/lib.rs index 596596b..52d66bc 100644 --- a/logging/src/lib.rs +++ b/logging/src/lib.rs @@ -7,6 +7,7 @@ mod test; use std::fmt::Display; use std::fs::OpenOptions; +use std::io; use std::io::Write; use config::log::LogSettings; @@ -24,45 +25,49 @@ use LogMessageType::GenericWarn; /** * Logs the given message. -* -* # Panics -* Panics if readlock on [`config::CONFIG`] could not be acquired -* or if another error occurs, such as a full disk. */ -pub fn log_message(msg: &LogMessage, conf: &LogSettings, file: &str, line: u32, column: u32) { +pub fn log_message(msg: &LogMessage, conf: &LogSettings, file: &str, line: u32, column: u32) -> Result<(), io::Error> { + // Check if message may be logged according to config. let Some(log_line) = log_to_str(msg, conf, file, line, column) else { - return; + return Ok(()); }; - // May panic if file cannot be opened or written to. - conf.path().as_ref().map_or_else( - || {}, - |path| { - let mut file = OpenOptions::new() + // Log to file + match conf.path().as_ref() + { + None => {/* Do not log to file */} + Some(p) => + { + let file = OpenOptions::new() .write(true) .append(true) .create(true) - .open(path) - .unwrap_or_else(|_| panic!("Could not open log file: {path:#?}")); - writeln!(file, "{log_line}") - .unwrap_or_else(|_| panic!("Could not write log to file: {path:#?}")); - }, - ); + .open(p); + let mut file = match file + { + Ok(f) => f, + Err(e) => return Err(e), + }; + match writeln!(file, "{log_line}") + { + Ok(_) => {}, + Err(e) => return Err(e), + } + } + }; if msg.1 <= Warning && *conf.stderr() { - // May panic if writing to stderr fails. - eprintln!("{log_line}"); + let mut stdout = io::stdout().lock(); + return writeln!(stdout, "{log_line}"); } else if msg.1 >= Information && *conf.stdout() { - // May panic if writing to stdout fails. - println!("{log_line}"); + let mut stderr = io::stderr().lock(); + return writeln!(stderr, "{log_line}") } + return Ok(()); } /** * Return log line, if message may be logged according to [`config::log::LogSettings`]. -* # Panics -* Panics if readlock on [`config::CONFIG`] could not be acquired -* or if another error occurs, such as a full disk. */ #[must_use] pub fn log_to_str( @@ -103,11 +108,12 @@ macro_rules! log { let conf = config::LOG_SETTINGS .read() .unwrap_or_else(|_| panic!("Failed aqcuire read lock on config!")); - log_message($msg, &*conf, file!(), line!(), column!()); + let res = log_message($msg, &*conf, file!(), line!(), column!()); drop(conf); + res }; ($msg:expr, $config:expr) => { - log_message($msg, $config, file!(), line!(), column!()); + log_message($msg, $config, file!(), line!(), column!()) }; } -- 2.45.2 From beb337cefd817938bc5023be30c3390be49f6845 Mon Sep 17 00:00:00 2001 From: Leon Wilzer Date: Mon, 6 May 2024 21:09:09 +0200 Subject: [PATCH 25/27] todo: trait --- logging/src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/logging/src/lib.rs b/logging/src/lib.rs index 52d66bc..e356e97 100644 --- a/logging/src/lib.rs +++ b/logging/src/lib.rs @@ -129,6 +129,8 @@ impl Display for LogMessage { } } +// TODO: Evaluate replacing the following with a trait, to decouple this component from the rest. + /** * Every possible Message, which may be logged. Grouped the following: * @@ -188,4 +190,4 @@ impl Display for LogMessageType { } } } -} +} \ No newline at end of file -- 2.45.2 From c2bade653aa9e65d8f48517dee9f74acde2aea05 Mon Sep 17 00:00:00 2001 From: Leon Wilzer Date: Thu, 9 May 2024 17:48:52 +0000 Subject: [PATCH 26/27] rustfmt --- logging/src/lib.rs | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/logging/src/lib.rs b/logging/src/lib.rs index e356e97..d73bebf 100644 --- a/logging/src/lib.rs +++ b/logging/src/lib.rs @@ -26,31 +26,33 @@ use LogMessageType::GenericWarn; /** * Logs the given message. */ -pub fn log_message(msg: &LogMessage, conf: &LogSettings, file: &str, line: u32, column: u32) -> Result<(), io::Error> { +pub fn log_message( + msg: &LogMessage, + conf: &LogSettings, + file: &str, + line: u32, + column: u32, +) -> Result<(), io::Error> { // Check if message may be logged according to config. let Some(log_line) = log_to_str(msg, conf, file, line, column) else { return Ok(()); }; // Log to file - match conf.path().as_ref() - { - None => {/* Do not log to file */} - Some(p) => - { + match conf.path().as_ref() { + None => { /* Do not log to file */ } + Some(p) => { let file = OpenOptions::new() .write(true) .append(true) .create(true) .open(p); - let mut file = match file - { + let mut file = match file { Ok(f) => f, Err(e) => return Err(e), }; - match writeln!(file, "{log_line}") - { - Ok(_) => {}, + match writeln!(file, "{log_line}") { + Ok(_) => {} Err(e) => return Err(e), } } @@ -61,7 +63,7 @@ pub fn log_message(msg: &LogMessage, conf: &LogSettings, file: &str, line: u32, return writeln!(stdout, "{log_line}"); } else if msg.1 >= Information && *conf.stdout() { let mut stderr = io::stderr().lock(); - return writeln!(stderr, "{log_line}") + return writeln!(stderr, "{log_line}"); } return Ok(()); } @@ -190,4 +192,4 @@ impl Display for LogMessageType { } } } -} \ No newline at end of file +} -- 2.45.2 From 16a30028f73a72b3854228bd13a6c821158a5f9a Mon Sep 17 00:00:00 2001 From: Leon Wilzer Date: Thu, 9 May 2024 20:22:24 +0200 Subject: [PATCH 27/27] hide test output to fix drone ci --- logging/src/test.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/logging/src/test.rs b/logging/src/test.rs index 08f8787..8293a72 100644 --- a/logging/src/test.rs +++ b/logging/src/test.rs @@ -191,6 +191,8 @@ fn log_macro_shared_config() { .expect("Could not acquire write lock on config!"); take(&mut *config); config.set_path(PathBuf::from(log_path)); + config.set_stdout(false); + config.set_stderr(false); drop(config); create_dir_all(PathBuf::from(LOG_DIR.as_str())) @@ -216,6 +218,8 @@ fn log_concurrently_any_order() { take(&mut *config); let mut messages = Vec::with_capacity(CONCURRENT_MESSAGE_COUNT); config.set_path(PathBuf::from(log_path)); + config.set_stdout(false); + config.set_stderr(false); drop(config); for i in 0..CONCURRENT_MESSAGE_COUNT { @@ -261,6 +265,8 @@ fn log_concurrently_correct_order() { take(&mut *config); let mut messages = Vec::with_capacity(CONCURRENT_MESSAGE_COUNT); config.set_path(PathBuf::from(log_path)); + config.set_stdout(false); + config.set_stderr(false); drop(config); for i in 0..CONCURRENT_MESSAGE_COUNT { -- 2.45.2