From fceef1ecc940d9926ea4481ccf4b722c4f264786 Mon Sep 17 00:00:00 2001 From: Intege-rs Date: Fri, 12 Sep 2025 05:18:21 -0400 Subject: [PATCH] . --- .gitignore | 1 + Cargo.toml | 8 ++++ src/lib.rs | 108 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 14 +++++++ 4 files changed, 131 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 src/lib.rs create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..7e72e5e --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "picolog" +version = "0.1.0" +edition = "2024" + +[dependencies] +itertools = "*" +log = { version = "*", features = ["std"] } \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..9f2f690 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,108 @@ +use std::io::{stdout, Write}; +use itertools::{Itertools, Position}; +use log::*; + +//╶───╴Initializers╶─────────────────────────────────────────────────────────╴ + +pub fn init() -> Result<(), SetLoggerError>{ + init_with_filter(LevelFilter::Info, |_|true) +} + +pub fn init_with_level(max_level_filter: LevelFilter) -> Result<(), SetLoggerError>{ + init_with_filter(max_level_filter, |_|true) +} + +pub fn init_with_filter( + max_level_filter: LevelFilter, + filter: impl Fn(&Metadata) -> bool + Send + Sync + 'static +) -> Result<(), SetLoggerError> { + set_boxed_logger(Box::new(PicoLogger { + filter, + }))?; + set_max_level(max_level_filter); + Ok(()) +} + +//╶───╴PicoLogger╶───────────────────────────────────────────────────────────╴ + +pub struct PicoLogger bool + Send + Sync> { + filter: F +} + +impl bool + Send + Sync> Log for PicoLogger { + fn enabled(&self, metadata: &Metadata) -> bool { (self.filter)(metadata) } + fn log(&self, record: &Record) { _=format(record); } + fn flush(&self) {} +} + +//╶───╴Formatting╶───────────────────────────────────────────────────────────╴ + +fn format(record: &Record) -> std::io::Result<()> { + let mut buffer = stdout(); + let buffer = &mut buffer; + write!(buffer, "{GRAY}")?; + + // module name + let mp = rtrunc(record.module_path().unwrap_or(""), 12); + write!(buffer, "{mp:>12} ")?; + + // file path + let fp = rtrunc(record.file().unwrap_or(""), 12); + write!(buffer, "{fp:>12}:{:<4}", record.line().unwrap_or(0))?; + + // write the record level + match record.level() { + Level::Error => write!(buffer, "[{RED}Errr{GRAY}] "), + Level::Warn => write!(buffer, "[{YELLOW}Warn{GRAY}] "), + Level::Info => write!(buffer, "[{BLUE}Info{GRAY}] "), + Level::Debug => write!(buffer, "[{PURPLE}Dbug{GRAY}] "), + Level::Trace => write!(buffer, "[{GREEN}Trce{GRAY}] "), + }?; + + // write the actual message + use std::fmt::Write; + let mut splicer = LineSplicer(buffer); + _=write!(splicer, "{}", record.args()); + + // finalize + write!(buffer, "\n")?; + buffer.flush() +} + +struct LineSplicer<'a>(&'a mut dyn Write); +impl<'a> std::fmt::Write for LineSplicer<'a> { + fn write_str(&mut self, s: &str) -> std::fmt::Result { + for (p, part) in s.split("\n").with_position() { + match p { + Position::First => write!(self.0, "\n {GRAY}╷{RESET} "), + Position::Middle => write!(self.0, "\n {GRAY}│{RESET} "), + Position::Last => write!(self.0, "\n {GRAY}╵{RESET} "), + Position::Only => write!(self.0, "{RESET}"), + }.map_err(|_|std::fmt::Error)?; + self.0.write(part.as_bytes()) + .map_err(|_|std::fmt::Error)?; + } + Ok(()) + } +} + +//╶───╴Utility╶──────────────────────────────────────────────────────────────╴ + +const RESET : &str = "\x1B[0m"; +const RED : &str = "\x1B[0;31m"; +const GREEN : &str = "\x1B[0;32m"; +const YELLOW : &str = "\x1B[0;33m"; +const BLUE : &str = "\x1B[0;34m"; +const PURPLE : &str = "\x1B[0;35m"; +const GRAY : &str = "\x1B[0;90m"; + +/// truncate strings to a maximum length, right aligned +fn rtrunc(string: &str, max_len: usize) -> &str { + let len = string.chars().count(); + if len <= max_len { return string; } + + let crop = string.char_indices() + .nth(len - max_len) + .unwrap_or((0,'\0')).0; + &string[crop..] +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..eb93be6 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,14 @@ +include!("lib.rs"); + +fn main() { + _=init_with_level(LevelFilter::Trace); + trace!("trace"); + debug!("debug"); + info!("info"); + warn!("warn"); + error!("error"); + + info!("multiline\ndemo"); + info!("one\ntwo\nthree\nfour"); + +} \ No newline at end of file