/// Enkel fil-logger. Loggar till /logs/.log /// Trådsäker via en global Mutex. use std::fs::{self, OpenOptions}; use std::io::Write; use std::path::PathBuf; use std::sync::Mutex; use std::time::SystemTime; static LOG: Mutex> = Mutex::new(None); /// Initialisera loggern. Anropas en gång från main(). pub fn init() { let dir = log_dir(); if let Err(e) = fs::create_dir_all(&dir) { eprintln!("log::init: kunde inte skapa logg-mapp {:?}: {}", dir, e); return; } let name = today_filename(); let path = dir.join(name); match OpenOptions::new().create(true).append(true).open(&path) { Ok(f) => { *LOG.lock().unwrap() = Some(f); log(&"=== TUI-WM startad ===".to_string()); } Err(e) => eprintln!("log::init: kunde inte öppna {:?}: {}", path, e), } } /// Skriv en logg-rad med tidsstämpel. pub fn log(msg: &str) { let ts = timestamp(); let line = format!("[{}] {}\n", ts, msg); if let Ok(mut guard) = LOG.lock() { if let Some(f) = guard.as_mut() { let _ = f.write_all(line.as_bytes()); let _ = f.flush(); } } } /// Makro för bekväm formatering — används som log!("foo {}", bar) #[macro_export] macro_rules! log { ($($arg:tt)*) => { $crate::log::log(&format!($($arg)*)) }; } // ─── Interna hjälpfunktioner ────────────────────────────────────────────────── fn log_dir() -> PathBuf { // Lägg logs/ bredvid den körande binären let exe = std::env::current_exe().unwrap_or_else(|_| PathBuf::from(".")); exe.parent().unwrap_or(std::path::Path::new(".")).join("logs") } fn today_filename() -> String { // Bygg YYYY-MM-DD från SystemTime utan externa beroenden let secs = SystemTime::now() .duration_since(SystemTime::UNIX_EPOCH) .map(|d| d.as_secs()) .unwrap_or(0); let (y, mo, d) = unix_to_ymd(secs); format!("{:04}-{:02}-{:02}.log", y, mo, d) } pub fn timestamp() -> String { let secs = SystemTime::now() .duration_since(SystemTime::UNIX_EPOCH) .map(|d| d.as_secs()) .unwrap_or(0); let subsec_ms = SystemTime::now() .duration_since(SystemTime::UNIX_EPOCH) .map(|d| d.subsec_millis()) .unwrap_or(0); let (y, mo, d) = unix_to_ymd(secs); let h = (secs % 86400) / 3600; let m = (secs % 3600) / 60; let s = secs % 60; format!("{:04}-{:02}-{:02} {:02}:{:02}:{:02}.{:03}", y, mo, d, h, m, s, subsec_ms) } /// Konverterar Unix-sekunder till (år, månad, dag). Ingen extern crate nödvändig. fn unix_to_ymd(secs: u64) -> (u32, u32, u32) { // Algoritm: https://howardhinnant.github.io/date_algorithms.html let z = (secs / 86400) as i64 + 719468; let era = if z >= 0 { z } else { z - 146096 } / 146097; let doe = (z - era * 146097) as u32; let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365; let y = yoe as i64 + era * 400; let doy = doe - (365 * yoe + yoe / 4 - yoe / 100); let mp = (5 * doy + 2) / 153; let d = doy - (153 * mp + 2) / 5 + 1; let m = if mp < 10 { mp + 3 } else { mp - 9 }; let y = if m <= 2 { y + 1 } else { y }; (y as u32, m, d) }