use super::log_level::LogLevel;
use chrono::Local;
use core::fmt;
use std::ffi::OsStr;
use std::fmt::Write;
use std::path::Path;
use std::path::PathBuf;
use whoami::{self, fallible};
const DEFAULT_TIMESTAMP_FORMAT: &str = "%Y-%m-%d %H:%M:%S%z";
const DEFAULT_MESSAGE_FORMAT: &str =
"{TIMESTAMP} | {EXE_NAME} | {SYSTEM_NAME} | {USER_NAME} | {LEVEL} | {MESSAGE}";
#[derive(Debug)]
pub enum ConfigError {
InvalidFormat(String),
}
impl fmt::Display for ConfigError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ConfigError::InvalidFormat(details) => write!(f, "Invalid format: {}", details),
}
}
}
impl std::error::Error for ConfigError {}
#[derive(Clone, Debug)]
pub struct LoggerConfiguration {
log_dir: PathBuf, file_extension: String, days_stored: Option<u32>, executions_stored: Option<u32>, filter_log_level: Option<LogLevel>, exe_name: String, system_name: String, user_name: String, message_format: Option<String>, timestamp_format: Option<String>, }
impl LoggerConfiguration {
pub fn new(
log_dir: PathBuf,
file_extension: &str,
days_stored: Option<u32>,
executions_stored: Option<u32>,
filter_log_level: Option<LogLevel>,
) -> Self {
let exe_name = match std::env::current_exe()
.ok()
.as_ref()
.map(Path::new)
.and_then(Path::file_name)
.and_then(OsStr::to_str)
.map(String::from)
{
Some(x) => x,
None => "Unknown".to_string(),
};
let system_name = match fallible::hostname() {
Ok(x) => x,
Err(_) => "Unknown".to_string(),
};
let user_domain = match std::env::var("USERDOMAIN") {
Ok(val) => format!("{}\\", val),
Err(_) => "".to_string(),
};
let user_name = match fallible::username() {
Ok(x) => format!("{}{}", user_domain, x),
Err(_) => "Unknown".to_string(),
};
LoggerConfiguration {
log_dir,
file_extension: file_extension.to_string(),
days_stored,
executions_stored,
filter_log_level,
exe_name,
system_name,
user_name,
message_format: None,
timestamp_format: None,
}
}
pub fn set_filter_level(&mut self, filter_level: LogLevel) {
self.filter_log_level = Some(filter_level);
}
pub fn get_filter_level(&self) -> Option<LogLevel> {
self.filter_log_level.clone()
}
pub fn get_message_format(&self) -> &str {
if let Some(x) = &self.message_format {
x
} else {
DEFAULT_MESSAGE_FORMAT
}
}
pub fn set_message_format(&mut self, format: &str) -> Result<(), ConfigError> {
if !format.contains("{MESSAGE}") {
eprintln!("Message format must contain {{MESSAGE}}. Message format is unchanged");
Err(ConfigError::InvalidFormat(
"Message format must contain {MESSAGE}".to_string(),
))
} else if !format.contains("{LEVEL}") {
eprintln!("Message format must contain {{LEVEL}}. Message format is unchanged");
Err(ConfigError::InvalidFormat(
"Message format must contain {LEVEL}".to_string(),
))
} else {
self.message_format = Some(format.to_string());
Ok(())
}
}
pub fn get_timestamp_format(&self) -> &str {
if let Some(x) = &self.timestamp_format {
x
} else {
DEFAULT_TIMESTAMP_FORMAT
}
}
pub fn set_timestamp_format(&mut self, format: &str) -> Result<(), ConfigError> {
let mut formatted_time = String::new();
let result = write!(formatted_time, "{}", Local::now().format(format));
if result.is_err() {
eprintln!("Invalid timestamp format. Timestamp format is unchanged");
Err(ConfigError::InvalidFormat(
"Invalid timestamp format".to_string(),
))
} else {
self.timestamp_format = Some(format.to_string());
Ok(())
}
}
pub fn get_system_name(&self) -> &str {
&self.system_name
}
pub fn get_exe_name(&self) -> &str {
&self.exe_name
}
pub fn get_user_name(&self) -> &str {
&self.user_name
}
pub fn get_log_dir(&self) -> &Path {
&self.log_dir
}
pub fn get_file_extension(&self) -> &str {
&self.file_extension
}
pub fn get_days_stored(&self) -> Option<u32> {
self.days_stored
}
pub fn get_executions_stored(&self) -> Option<u32> {
self.executions_stored
}
}
impl Default for LoggerConfiguration {
fn default() -> Self {
LoggerConfiguration::new(
PathBuf::from("./logs"), "txt", None, None, None, )
}
}