From 62e054c7fec5ac9e0ccaf20a9aa1ad6bc81d8605 Mon Sep 17 00:00:00 2001 From: Vladimir Rubin Date: Thu, 24 Apr 2025 15:18:13 +0300 Subject: [PATCH] feat: logging via env_logger and log crates --- Cargo.lock | 179 ++++++++++++++++++++++++++++++++++++++++++++++++++-- Cargo.toml | 10 ++- src/lib.rs | 28 ++++++-- src/main.rs | 101 +++++++++++++++++++++-------- 4 files changed, 277 insertions(+), 41 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b7391f3..f160cf6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,15 @@ 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" @@ -32,6 +41,56 @@ 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" @@ -277,6 +336,12 @@ 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" @@ -344,6 +409,29 @@ 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" @@ -357,7 +445,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -453,6 +541,8 @@ name = "hinoirisetr" version = "0.1.0" dependencies = [ "chrono", + "env_logger", + "log", "notify-rust", "tokio", ] @@ -491,6 +581,36 @@ 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" @@ -716,6 +836,21 @@ 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" @@ -764,6 +899,35 @@ 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" @@ -780,7 +944,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.4.15", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -793,7 +957,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.9.4", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -906,7 +1070,7 @@ dependencies = [ "getrandom", "once_cell", "rustix 1.0.5", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -959,6 +1123,7 @@ dependencies = [ "libc", "mio", "pin-project-lite", + "signal-hook-registry", "socket2", "tokio-macros", "windows-sys 0.52.0", @@ -1040,6 +1205,12 @@ 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 4b3c468..155e74d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,15 +1,21 @@ [package] name = "hinoirisetr" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] chrono = "0.4.40" +env_logger = "0.11.8" +log = "0.4.27" notify-rust = "4.11.7" -tokio = { version = "1", features = ["rt-multi-thread", "macros", "sync", "time","io-util","net" ] } +tokio = { version = "1", features = ["rt-multi-thread", "macros", "sync", "time","io-util","net","signal" ] } [profile.release] strip = true lto = true codegen-units = 1 opt-level = "s" +panic = "abort" + +[profile.release.package."*"] +opt-level = "z" diff --git a/src/lib.rs b/src/lib.rs index d6d7472..b67f935 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,9 +1,12 @@ //! hinoirisetr library //! Contains core logic for computing temperature and gamma and applying settings. -use chrono::{DateTime, Local, Timelike}; use std::process::Command; +use chrono::{DateTime, Local, Timelike}; + +use log::{debug, trace}; + // Constants for colors and timings pub const TEMP_DAY: i32 = 6500; pub const TEMP_NIGHT: i32 = 2700; @@ -13,49 +16,60 @@ pub const GAMMA_NIGHT: i32 = 95; pub const SUNSET_START: u32 = 19; pub const SUNSET_END: u32 = 22; -pub const SUNRISE_START: u32 = 4; -pub const SUNRISE_END: u32 = 7; +pub const SUNRISE_START: u32 = 14; +pub const SUNRISE_END: u32 = 17; /// Linearly interpolate between start and end by factor [0.0, 1.0] pub fn interpolate(start: i32, end: i32, factor: f64) -> i32 { + trace!("interpolate({start}, {end}, {factor})"); (start as f64 + (end - start) as f64 * factor).round() as i32 } /// Compute current temperature and gamma based on provided time pub fn compute_settings(now: DateTime) -> (i32, i32) { + trace!("compute_settings({now:?})"); let time_in_hours = now.hour() as f64 + now.minute() as f64 / 60.0; + trace!("time_in_hours: {time_in_hours}"); if (time_in_hours >= SUNSET_START as f64) && (time_in_hours <= SUNSET_END as f64) { - let factor = ((time_in_hours - SUNSET_START as f64) - / (SUNSET_END - SUNSET_START) as f64) + trace!("time_in_hours is within sunset"); + let factor = ((time_in_hours - SUNSET_START as f64) / (SUNSET_END - SUNSET_START) as f64) .clamp(0.0, 1.0); ( interpolate(TEMP_DAY, TEMP_NIGHT, factor), interpolate(GAMMA_DAY, GAMMA_NIGHT, factor), ) } else if (time_in_hours >= SUNRISE_START as f64) && (time_in_hours <= SUNRISE_END as f64) { + trace!("time_in_hours is within sunrise"); let factor = 1.0 - - ((time_in_hours - SUNRISE_START as f64) - / (SUNRISE_END - SUNRISE_START) as f64) + - ((time_in_hours - SUNRISE_START as f64) / (SUNRISE_END - SUNRISE_START) as f64) .clamp(0.0, 1.0); ( interpolate(TEMP_DAY, TEMP_NIGHT, factor), interpolate(GAMMA_DAY, GAMMA_NIGHT, factor), ) } else if time_in_hours > SUNSET_END as f64 || time_in_hours < SUNRISE_START as f64 { + trace!("time_in_hours is within night"); (TEMP_NIGHT, GAMMA_NIGHT) } else { + trace!("time_in_hours is within day"); (TEMP_DAY, GAMMA_DAY) } } /// Apply given temperature (Kelvin) and gamma (%) via hyprctl commands pub fn apply_settings(temp: i32, gamma: i32) { + trace!("apply_settings({temp}, {gamma})"); + debug!("applying temperature: {temp}"); + debug!("applying gamma: {gamma}"); + let _ = Command::new("hyprctl") .args(["hyprsunset", "temperature", &temp.to_string()]) .output(); + trace!("hyprctl hyprsunset temperature {temp}"); let _ = Command::new("hyprctl") .args(["hyprsunset", "gamma", &gamma.to_string()]) .output(); + trace!("hyprctl hyprsunset gamma {gamma}"); } diff --git a/src/main.rs b/src/main.rs index fa9e87f..40ad8f4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,50 +1,58 @@ +use std::sync::Arc; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::time::Duration; + use chrono::Local; use hinoirisetr::{apply_settings, compute_settings}; +use log::{debug, error, info, trace, warn}; use notify_rust::Notification; -use std::sync::{ - atomic::{AtomicBool, Ordering}, - Arc, -}; -use std::time::Duration; -use tokio::{ - io::{AsyncBufReadExt, BufReader}, - net::UnixListener, - sync::Notify, - time::sleep, -}; +use tokio::io::{AsyncBufReadExt, BufReader}; +use tokio::net::UnixListener; +use tokio::signal::unix::{SignalKind, signal}; +use tokio::sync::Notify; +use tokio::time::sleep; const SOCKET_PATH: &str = "/tmp/hinoirisetr.sock"; const NOTIFICATION_TIMEOUT: u32 = 5000; async fn socket_server(disabled: Arc, notify: Arc) { - let _ = std::fs::remove_file(SOCKET_PATH); let listener = UnixListener::bind(SOCKET_PATH).expect("Failed to bind socket"); + trace!("socket server bound"); loop { let (stream, _) = listener.accept().await.unwrap(); let mut lines = BufReader::new(stream).lines(); + trace!("socket server accepted connection"); + if let Ok(Some(line)) = lines.next_line().await { match line.trim() { "disable" => { + trace!("disable dispatched"); disabled.store(true, Ordering::SeqCst); notify.notify_one(); + debug!("dimming is disabled"); } "enable" => { + trace!("enable dispatched"); disabled.store(false, Ordering::SeqCst); notify.notify_one(); + debug!("dimming is enabled"); } "toggle" => { + trace!("toggle dispatched"); let now = !disabled.load(Ordering::SeqCst); disabled.store(now, Ordering::SeqCst); notify.notify_one(); + debug!("dimming is {}", if now { "enabled" } else { "disabled" }); } "status" => { + trace!("status dispatched"); // compute current temp/gamma let now = Local::now(); let (cur_temp, cur_gamma) = compute_settings(now); - println!( + info!( "dimming is {} — temp: {}K, gamma: {}%", if disabled.load(Ordering::SeqCst) { "disabled" @@ -56,13 +64,14 @@ async fn socket_server(disabled: Arc, notify: Arc) { ); } "status_notify" => { + trace!("status_notify dispatched"); let now = Local::now(); let (cur_temp, cur_gamma) = compute_settings(now); let body = if disabled.load(Ordering::SeqCst) { "disabled".to_string() } else { - format!("temp: {}K, gamma: {}%", cur_temp, cur_gamma) + format!("temp: {cur_temp}K, gamma: {cur_gamma}%") }; match Notification::new() @@ -74,11 +83,11 @@ async fn socket_server(disabled: Arc, notify: Arc) { { Ok(_) => {} Err(err) => { - eprintln!("Erorr occured while sending a notification: {}", err) + error!("Erorr occured while sending a notification: {err}") } } } - _ => eprintln!("unknown command: {}", line.trim()), + _ => error!("unknown command: {}", line.trim()), } } } @@ -86,10 +95,24 @@ async fn socket_server(disabled: Arc, notify: Arc) { #[tokio::main] async fn main() { + env_logger::init(); + log::info!("starting the daemon"); + let disabled = Arc::new(AtomicBool::new(false)); let notify = Arc::new(Notify::new()); - println!("dimming is enabled"); + if std::path::Path::new(SOCKET_PATH).exists() { + match std::os::unix::net::UnixStream::connect(SOCKET_PATH) { + Ok(_) => { + error!("Another instance is running."); + std::process::exit(1); + } + Err(_) => { + warn!("Stale socket found, removing."); + let _ = std::fs::remove_file(SOCKET_PATH); + } + } + } // Spawn control socket server { @@ -100,19 +123,41 @@ async fn main() { }); } - loop { - if disabled.load(Ordering::SeqCst) { - apply_settings(hinoirisetr::TEMP_DAY, hinoirisetr::GAMMA_DAY); - } else { - let now = Local::now(); - let (temp, gamma) = compute_settings(now); + // Signal handling + let mut sigint = signal(SignalKind::interrupt()).unwrap(); + let mut sigterm = signal(SignalKind::terminate()).unwrap(); - apply_settings(temp, gamma); - } + // Main loop with shutdown support + tokio::select! { + _ = async { + loop { + if disabled.load(Ordering::SeqCst) { + apply_settings(hinoirisetr::TEMP_DAY, hinoirisetr::GAMMA_DAY); + } else { + let now = Local::now(); + let (temp, gamma) = compute_settings(now); + apply_settings(temp, gamma); + } - tokio::select! { - _ = sleep(Duration::from_secs(300)) => {}, - _ = notify.notified() => {}, + tokio::select! { + _ = sleep(Duration::from_secs(300)) => {trace!("main loop tick");}, + _ = notify.notified() => {trace!("main loop woke up via notify");}, + } + } + } => {}, + _ = sigint.recv() => { + info!("Received SIGINT, shutting down..."); + }, + _ = sigterm.recv() => { + info!("Received SIGTERM, shutting down..."); + }, + } + + // Cleanup the socket file on shutdown + if std::path::Path::new(SOCKET_PATH).exists() { + match std::fs::remove_file(SOCKET_PATH) { + Ok(_) => info!("Socket file {SOCKET_PATH} removed."), + Err(e) => warn!("Failed to remove socket file {SOCKET_PATH}: {e}"), } } }