flogging 0.6.0

This is a fast, flexible logging crate, loosely based on the Java logging package: java.util.logging.
Documentation
//
// File Name:    level.rs
// Directory:    src/logger
// Project Name: flogging
//
// Copyright (C) 2025 Bradley Willcott
//
// SPDX-License-Identifier: GPL-3.0-or-later
//
// This library (crate) is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This library (crate) is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this library (crate).  If not, see <https://www.gnu.org/licenses/>.
//

//!
//! # Log Entry Level
//!
//! This module provides the enum containing the possible log entry levels.
//! The order in which they are listed within the enum, enables logging at that level
//! and at all higher levels.
//!
//! The levels in descending order are:
//!
//! - SEVERE (highest level)
//! - WARNING
//! - INFO
//! - CONFIG
//! - FINE
//! - FINER
//! - FINEST (lowest level)
//!
//! In addition there is a level **OFF** that can be used to turn off logging.

use std::{fmt, str::FromStr};
use strum::{EnumIter, IntoEnumIterator};

///
/// Log entry level setting.
///
/// Default level: INFO.
///
// #[allow(unused)]
#[derive(Debug, Clone, Default, PartialEq, PartialOrd, EnumIter, Copy)]
pub enum Level {
    ///
    /// ALL is a special level that can be used to turn on logging
    /// for all levels.
    ///
    ALL,

    ///
    /// FINEST indicates a highly detailed tracing message.
    ///
    FINEST,

    ///
    /// FINER indicates a fairly detailed tracing message.
    /// Suggest logging calls for entering, returning,
    /// or `Error`s, such as returned via `Result`, are traced at
    /// this level.
    ///
    FINER,

    ///
    /// FINE is a message level providing tracing information.
    ///
    /// All of FINE, FINER, and FINEST are intended for relatively
    /// detailed tracing. The exact meaning of the three levels will
    /// vary between subsystems, but in general, FINEST should be
    /// used for the most voluminous detailed output, FINER for somewhat
    /// less detailed output, and FINE for the lowest volume (and most
    /// important) messages.
    ///
    /// In general the FINE level should be used for information that
    /// will be broadly interesting to developers who do not have a
    /// specialized interest in the specific subsystem.
    ///
    /// FINE messages might include things like minor (recoverable)
    /// failures. Issues indicating potential performance problems are
    /// also worth logging as FINE.
    ///
    FINE,

    ///
    /// CONFIG is a message level for static configuration messages.
    ///
    /// CONFIG messages are intended to provide a variety of static
    /// configuration information, to assist in debugging problems
    /// that may be associated with particular configurations.
    ///
    /// For example, a CONFIG message might include the CPU type, the
    /// graphics depth, the GUI look-and-feel, etc.
    ///
    CONFIG,

    ///
    /// INFO is a message level for informational messages.
    ///
    /// Typically INFO messages will be written to the console or its
    /// equivalent. So the INFO level should only be used for reasonably
    /// significant messages that will make sense to end users and system
    /// administrators.
    ///
    /// \[default level]
    ///
    #[default]
    INFO,

    ///
    /// WARNING is a message level indicating a potential problem.
    ///
    /// In general WARNING messages should describe events that will be
    /// of interest to end users or system managers, or which indicate
    /// potential problems.
    ///
    WARNING,

    ///
    /// SEVERE is a message level indicating a serious failure.
    ///
    /// In general SEVERE messages should describe events that are of
    /// considerable importance and which will prevent normal program
    /// execution. They should be reasonably intelligible to end users
    /// and to system administrators.
    ///
    SEVERE,

    ///
    /// OFF is a special level that can be used to turn off logging.
    ///
    OFF,
}

impl Level {
    ///
    /// Converts a level to its string version.
    ///
    pub const fn as_str(&self) -> &'static str {
        match self {
            Level::ALL => "ALL",
            Level::FINEST => "FINEST",
            Level::FINER => "FINER",
            Level::FINE => "FINE",
            Level::CONFIG => "CONFIG",
            Level::INFO => "INFO",
            Level::WARNING => "WARNING",
            Level::SEVERE => "SEVERE",
            Level::OFF => "OFF",
        }
    }
}

impl fmt::Display for Level {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.as_str().fmt(f)
    }
}

#[allow(dead_code)]
#[derive(Debug)]
pub struct LevelError {
    msg: String,
}

impl FromStr for Level {
    type Err = LevelError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "ALL" => Ok(Level::ALL),
            "FINEST" => Ok(Level::FINEST),
            "FINER" => Ok(Level::FINER),
            "FINE" => Ok(Level::FINE),
            "CONFIG" => Ok(Level::CONFIG),
            "INFO" => Ok(Level::INFO),
            "WARNING" => Ok(Level::WARNING),
            "SEVERE" => Ok(Level::SEVERE),
            "OFF" => Ok(Level::OFF),
            _ => Err(LevelError {
                msg: format!("Unknown Level: {s}"),
            }),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn compare_levels() {
        let log_level = Level::default();
        let b = Level::FINE;

        println!("\n|{log_level}|\n");

        assert!(b < log_level);
    }

    #[test]
    fn check_conversions() {
        for level in Level::iter() {
            let lstr = level.as_str();
            let l = Level::from_str(lstr).unwrap();
            assert_eq!(level, l);
        }

        assert!(Level::from_str("DEBUG").is_err());
    }
}