diff --git a/Cargo.lock b/Cargo.lock index 32d6657..b0a9e39 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,15 +17,6 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" -[[package]] -name = "aho-corasick" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" -dependencies = [ - "memchr", -] - [[package]] name = "android-tzdata" version = "0.1.1" @@ -41,56 +32,6 @@ dependencies = [ "libc", ] -[[package]] -name = "anstream" -version = "0.6.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is_terminal_polyfill", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" - -[[package]] -name = "anstyle-parse" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" -dependencies = [ - "windows-sys 0.59.0", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" -dependencies = [ - "anstyle", - "once_cell", - "windows-sys 0.59.0", -] - [[package]] name = "async-broadcast" version = "0.7.2" @@ -336,12 +277,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "colorchoice" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" - [[package]] name = "concurrent-queue" version = "2.5.0" @@ -409,29 +344,6 @@ dependencies = [ "syn", ] -[[package]] -name = "env_filter" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" -dependencies = [ - "log", - "regex", -] - -[[package]] -name = "env_logger" -version = "0.11.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" -dependencies = [ - "anstream", - "anstyle", - "env_filter", - "jiff", - "log", -] - [[package]] name = "equivalent" version = "1.0.2" @@ -541,8 +453,6 @@ name = "hinoirisetr" version = "0.1.0" dependencies = [ "chrono", - "env_logger", - "log", "notify-rust", "serde", "tokio", @@ -583,36 +493,6 @@ dependencies = [ "hashbrown", ] -[[package]] -name = "is_terminal_polyfill" -version = "1.70.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" - -[[package]] -name = "jiff" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a064218214dc6a10fbae5ec5fa888d80c45d611aba169222fc272072bf7aef6" -dependencies = [ - "jiff-static", - "log", - "portable-atomic", - "portable-atomic-util", - "serde", -] - -[[package]] -name = "jiff-static" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "199b7932d97e325aff3a7030e141eafe7f2c6268e1d1b24859b753a627f45254" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "js-sys" version = "0.3.77" @@ -838,21 +718,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "portable-atomic" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" - -[[package]] -name = "portable-atomic-util" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" -dependencies = [ - "portable-atomic", -] - [[package]] name = "powerfmt" version = "0.2.0" @@ -901,35 +766,6 @@ version = "5.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" -[[package]] -name = "regex" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" - [[package]] name = "rustc-demangle" version = "0.1.24" @@ -1233,12 +1069,6 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" -[[package]] -name = "utf8parse" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" diff --git a/Cargo.toml b/Cargo.toml index 613a78d..d0a0041 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,8 +5,6 @@ edition = "2024" [dependencies] chrono = "0.4.40" -env_logger = "0.11.8" -log = "0.4.27" notify-rust = "4.11.7" serde = { version = "1.0", features = ["derive"] } toml = "0.8" diff --git a/src/lib.rs b/src/lib.rs index 9e86245..1b26164 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,9 +6,10 @@ use std::path::Path; use std::process::Command; use chrono::{DateTime, Local, Timelike}; -use log::{debug, trace}; use serde::Deserialize; +pub mod log; + #[derive(Debug, Deserialize, Copy, Clone)] #[serde(default)] pub struct Config { diff --git a/src/log.rs b/src/log.rs new file mode 100644 index 0000000..5e66675 --- /dev/null +++ b/src/log.rs @@ -0,0 +1,200 @@ +use std::fmt::{Display, Formatter}; +use std::sync::atomic::{AtomicU8, Ordering}; +use std::time::{SystemTime, UNIX_EPOCH}; + +/// Levels of logging verbosity +#[derive(PartialEq, PartialOrd, Ord, Eq, Debug, Clone, Copy)] +pub enum LogLevel { + Error = 1, + Warn = 2, + Info = 3, + Debug = 4, + Trace = 5, +} + +static GLOBAL_LOG_LEVEL: AtomicU8 = AtomicU8::new(LogLevel::Info as u8); + +// Check if a year is a leap year +fn is_leap_year(year: u32) -> bool { + (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0) +} + +// Convert seconds since epoch to YYYY-MM-DD HH:MM:SS (UTC) +fn format_timestamp(seconds: u64) -> String { + let mut days = (seconds / 86_400) as u32; // Total days since epoch + let seconds_in_day = (seconds % 86_400) as u32; + let hours = seconds_in_day / 3600; + let minutes = (seconds_in_day % 3600) / 60; + let seconds = seconds_in_day % 60; + + // Calculate year + let mut year = 1970; + while days > 0 { + let days_in_year = if is_leap_year(year) { 366 } else { 365 }; + if days >= days_in_year { + days -= days_in_year; + year += 1; + } else { + break; + } + } + + // Month lengths (non-leap and leap year variants) + let month_lengths = if is_leap_year(year) { + [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] + } else { + [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] + }; + + // Calculate month and day + let mut month = 1; + for &len in month_lengths.iter() { + if days < len { + break; + } + days -= len; + month += 1; + } + let day = days + 1; // Days are 1-based + + format!( + "{:04}-{:02}-{:02} {:02}:{:02}:{:02}", + year, month, day, hours, minutes, seconds + ) +} + +/// Set the global log level at runtime +pub fn set_log_level(level: LogLevel) { + GLOBAL_LOG_LEVEL.store(level as u8, Ordering::Relaxed); +} + +/// Get the current global log level +pub fn get_log_level() -> LogLevel { + match GLOBAL_LOG_LEVEL.load(Ordering::Relaxed) { + 1 => LogLevel::Error, + 2 => LogLevel::Warn, + 3 => LogLevel::Info, + 4 => LogLevel::Debug, + 5 => LogLevel::Trace, + _ => LogLevel::Info, + } +} + +impl Display for LogLevel { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + // ANSI color codes for terminal output + const RED: &str = "\x1b[31m"; // Error + const YELLOW: &str = "\x1b[33m"; // Warn + const GREEN: &str = "\x1b[32m"; // Info + const CYAN: &str = "\x1b[36m"; // Debug + const MAGENTA: &str = "\x1b[35m"; // Trace + const RESET: &str = "\x1b[0m"; // Reset color + + match self { + LogLevel::Error => write!(f, "{}[{:>5}]{}", RED, "ERROR", RESET), + LogLevel::Warn => write!(f, "{}[{:>5}]{}", YELLOW, "WARN", RESET), + LogLevel::Info => write!(f, "{}[{:>5}]{}", GREEN, "INFO", RESET), + LogLevel::Debug => write!(f, "{}[{:>5}]{}", CYAN, "DEBUG", RESET), + LogLevel::Trace => write!(f, "{}[{:>5}]{}", MAGENTA, "TRACE", RESET), + } + } +} + +/// Internal logger: prints if level is <= current global level +#[doc(hidden)] +pub fn log_internal(level: LogLevel, file: &str, line: u32, msg: &str) { + if (level as u8) <= (get_log_level() as u8) { + let timestamp = format_timestamp( + SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_secs(), + ); + if level == LogLevel::Trace { + println!("{} |{timestamp}| {file}:{line} - {msg}", LogLevel::Trace); + } else { + println!("{level} |{timestamp}| - {msg}"); + } + } +} + +/// Log an error-level message +#[macro_export] +macro_rules! error { + ($($arg:tt)+) => { + $crate::log::log_internal( + $crate::log::LogLevel::Error, + file!(), + line!(), + &format!($($arg)+), + ); + }; +} + +/// Log a warning-level message +#[macro_export] +macro_rules! warn { + ($($arg:tt)+) => { + $crate::log::log_internal( + $crate::log::LogLevel::Warn, + file!(), + line!(), + &format!($($arg)+), + ); + }; +} + +/// Log an info-level message +#[macro_export] +macro_rules! info { + ($($arg:tt)+) => { + $crate::log::log_internal( + $crate::log::LogLevel::Info, + file!(), + line!(), + &format!($($arg)+), + ); + }; +} + +/// Log a debug-level message (only in debug builds) +#[macro_export] +#[cfg(debug_assertions)] +macro_rules! debug { + ($($arg:tt)+) => { + $crate::log::log_internal( + $crate::log::LogLevel::Debug, + file!(), + line!(), + &format!($($arg)+), + ); + }; +} + +/// No-op debug in release builds +#[macro_export] +#[cfg(not(debug_assertions))] +macro_rules! debug { + ($($arg:tt)+) => {}; +} + +/// Log a trace-level message (only in debug builds) +#[macro_export] +#[cfg(debug_assertions)] +macro_rules! trace { + ($($arg:tt)+) => { + $crate::log::log_internal( + $crate::log::LogLevel::Trace, + file!(), + line!(), + &format!($($arg)+), + ); + }; +} + +/// No-op trace in release builds +#[macro_export] +#[cfg(not(debug_assertions))] +macro_rules! trace { + ($($arg:tt)+) => {}; +} diff --git a/src/main.rs b/src/main.rs index 06badfc..dd5a432 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,8 +5,7 @@ use std::sync::{Arc, OnceLock}; use std::time::Duration; use chrono::Local; -use hinoirisetr::{Config, apply_settings, compute_settings}; -use log::{debug, error, info, trace, warn}; +use hinoirisetr::{apply_settings, compute_settings, debug, error, info, trace, warn, Config}; use notify_rust::Notification; use tokio::io::{AsyncBufReadExt, BufReader}; use tokio::net::UnixListener; @@ -100,8 +99,13 @@ async fn socket_server(disabled: Arc, notify: Arc) { #[tokio::main] async fn main() { - env_logger::init(); - log::info!("starting the daemon"); + info!("starting the daemon"); + warn!("log level: {:?}", hinoirisetr::log::get_log_level()); + error!("log level: {:?}", hinoirisetr::log::get_log_level()); + trace!("log level: {:?}", hinoirisetr::log::get_log_level()); + debug!("log level: {:?}", hinoirisetr::log::get_log_level()); + + hinoirisetr::log::set_log_level(hinoirisetr::log::LogLevel::Trace); if !is_binary_available("hyprctl") { error!("hyprctl is not available, exiting.");