Compare commits

..

No commits in common. "d330a48c55c5704709ad8466af74537e712cb7c1" and "bd4e7eadcba97045ca49f3cb68ab362835566419" have entirely different histories.

5 changed files with 1 additions and 319 deletions

View File

@ -1,5 +1,3 @@
workspace = { members = ["config", "logging"] }
[package] [package]
name = "wanessa" name = "wanessa"
version = "0.1.0" version = "0.1.0"
@ -19,4 +17,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 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_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_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_errors_doc = "deny" # https://rust-lang.github.io/rust-clippy/master/index.html#/missing_assert_message

View File

@ -1,9 +0,0 @@
[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"

View File

@ -1,148 +0,0 @@
/*!
* 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`].<br>
/// Default: [`Warning`]
pub log_verbosity: LogVerbosity,
/// Logs UTC time and date of message, if true.<br>
/// Default: `false`
pub log_time: bool,
/// Time and date format.<br>
/// Defaults to `%F/%T:%f`.<br>
/// See [chrono](https://docs.rs/chrono/latest/chrono/format/strftime/index.html).<br>
/// Default example : `2001-12-31/23:59:33:26490000`
#[getset(skip)]
pub log_time_format: Option<String>,
/// Logs location in code, where the message was logged, if true.<br>
/// Default: `false`
pub log_location: bool,
/// If `Some(path)` tries to also write the log to `path` in addition to stderr/stderr.<br>
/// Default: [None]
pub log_path: Option<PathBuf>,
/// Logs to standard out, if true.<br>
/// Default: `true`
pub log_stdout: bool,
/// Logs to standard err, if true.<br>
/// Default: `true`
pub log_stderr: bool,
/// Database connection address.
/// Is an option to allow constructing a default config during compile time.<br>
/// Default: `localhost`.
#[getset(skip)]
pub db_addr: Option<String>,
/// Database connection port.<br>
/// 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<Config> = 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<Ordering>
{
(*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))
}
}

View File

@ -1,10 +0,0 @@
[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"}

View File

@ -1,149 +0,0 @@
/*!
* 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}") },
}
}
}