satlog/lib.rs
1//! `satlog.rs` is a simple and minimalistic logger designed for SAT solvers and the likes,
2//! following the `DIMACS` syntax of comments starting with `c`.
3//!
4//! The main interaction and setup is done through the [SatLogger] struct.
5//!
6//! This plugs into the generic [`log`](https://crates.io/crates/log) crate and simply displays
7//! the messages to `stdout`.
8//!
9//! Some neat features:
10//! - Messages with level [Level::Info] are displayed without prefix. This allows to show "default"
11//! logging messages and toggle them with the [LevelFilter::Off].
12//! - Opt-out colors with the `color` feature.
13//!
14//! # Example
15//!
16//! ```
17//! use satlog::SatLogger;
18//! use log::LevelFilter;
19//!
20//! fn main() {
21//! SatLogger::init(LevelFilter::Info);
22//!
23//! log::info!("Hello");
24//! log::trace!("Will not display");
25//! }
26//! ```
27use std::fmt::Display;
28
29use log::LevelFilter;
30use log::Log;
31use log::SetLoggerError;
32
33/// The main logger struct
34pub struct SatLogger {
35 /// All messages below or equal to the [LevelFilter] will be ignored.
36 level: LevelFilter,
37}
38
39fn write_record<S: Display>(levelstr: &S, record: &log::Record) {
40 for line in record.args().to_string().lines() {
41 println!("c {} {}", levelstr, line);
42 }
43}
44
45impl Log for SatLogger {
46 fn enabled(&self, metadata: &log::Metadata) -> bool {
47 metadata.level() <= self.level
48 }
49
50 fn log(&self, record: &log::Record) {
51 if self.enabled(record.metadata()) {
52 let level = record.level();
53 if matches!(level, log::Level::Info) {
54 for line in record.args().to_string().lines() {
55 println!("c {}", line);
56 }
57 } else {
58 #[cfg(feature = "color")]
59 {
60 use log::Level;
61 use colored::Colorize;
62
63 let levelstring = level.to_string();
64 let levelstr = levelstring.as_str();
65 let levelstr = match level {
66 Level::Info => unreachable!(),
67 Level::Warn => levelstr.yellow(),
68 Level::Error => levelstr.red(),
69 Level::Debug => levelstr.blue(),
70 Level::Trace => levelstr.purple(),
71 };
72 write_record(&levelstr, record);
73 }
74
75 #[cfg(not(feature = "color"))]
76 {
77 write_record(&record.level(), record);
78 }
79 }
80 }
81 }
82
83 fn flush(&self) {}
84}
85
86impl SatLogger {
87 /// Initializes and sets up a new SatLogger instance.
88 ///
89 /// # Arguments
90 ///
91 /// - `level`: Maximum level of messages to display. Any message with this or below level will
92 /// be ignored.
93 ///
94 /// # Example
95 ///
96 /// ```
97 /// use satlog::SatLogger;
98 /// use log::LevelFilter;
99 ///
100 /// fn main() {
101 /// SatLogger::init(LevelFilter::Info);
102 ///
103 /// log::info!("Hello");
104 /// log::trace!("Will not display");
105 /// }
106 /// ```
107 pub fn init(level: LevelFilter) -> Result<(), SetLoggerError> {
108 let logger = SatLogger { level };
109 log::set_boxed_logger(Box::new(logger)).map(|()| log::set_max_level(level))
110 }
111}