satlog 0.2.1

A logger for SAT-like solvers
Documentation
//! `satlog.rs` is a simple and minimalistic logger designed for SAT solvers and the likes,
//! following the `DIMACS` syntax of comments starting with `c`.
//!
//! The main interaction and setup is done through the [SatLogger] struct.
//!
//! This plugs into the generic [`log`](https://crates.io/crates/log) crate and simply displays
//! the messages to `stdout`.
//!
//! Some neat features:
//! - Messages with level [Level::Info] are displayed without prefix. This allows to show "default"
//!   logging messages and toggle them with the [LevelFilter::Off].
//! - Opt-out colors with the `color` feature.
//!
//! # Example
//!
//! ```
//! use satlog::SatLogger;
//! use log::LevelFilter;
//!
//! fn main() {
//!     SatLogger::init(LevelFilter::Info);
//!
//!     log::info!("Hello");
//!     log::trace!("Will not display");
//! }
//! ```
use std::fmt::Display;

use log::LevelFilter;
use log::Log;
use log::SetLoggerError;

/// The main logger struct
pub struct SatLogger {
    /// All messages below or equal to the [LevelFilter] will be ignored.
    level: LevelFilter,
}

fn write_record<S: Display>(levelstr: &S, record: &log::Record) {
    for line in record.args().to_string().lines() {
        println!("c {} {}", levelstr, line);
    }
}

impl Log for SatLogger {
    fn enabled(&self, metadata: &log::Metadata) -> bool {
        metadata.level() <= self.level
    }

    fn log(&self, record: &log::Record) {
        if self.enabled(record.metadata()) {
            let level = record.level();
            if matches!(level, log::Level::Info) {
                for line in record.args().to_string().lines() {
                    println!("c {}", line);
                }
            } else {
                #[cfg(feature = "color")]
                {
                    use log::Level;
                    use colored::Colorize;

                    let levelstring = level.to_string();
                    let levelstr = levelstring.as_str();
                    let levelstr = match level {
                        Level::Info => unreachable!(),
                        Level::Warn => levelstr.yellow(),
                        Level::Error => levelstr.red(),
                        Level::Debug => levelstr.blue(),
                        Level::Trace => levelstr.purple(),
                    };
                    write_record(&levelstr, record);
                }

                #[cfg(not(feature = "color"))]
                {
                    write_record(&record.level(), record);
                }
            }
        }
    }

    fn flush(&self) {}
}

impl SatLogger {
    /// Initializes and sets up a new SatLogger instance.
    ///
    /// # Arguments
    ///
    /// - `level`: Maximum level of messages to display. Any message with this or below level will
    ///            be ignored.
    ///
    /// # Example
    ///
    /// ```
    /// use satlog::SatLogger;
    /// use log::LevelFilter;
    ///
    /// fn main() {
    ///     SatLogger::init(LevelFilter::Info);
    ///
    ///     log::info!("Hello");
    ///     log::trace!("Will not display");
    /// }
    /// ```
    pub fn init(level: LevelFilter) -> Result<(), SetLoggerError> {
        let logger = SatLogger { level };
        log::set_boxed_logger(Box::new(logger)).map(|()| log::set_max_level(level))
    }
}