errsight 0.1.1

Rust client for ErrSight error tracking — captures panics, errors, and log/tracing events and ships them to the ErrSight API from a background thread.
Documentation
//! Severity levels.
//!
//! Mirrors the five-level ladder the ErrSight backend accepts
//! (`debug`, `info`, `warning`, `error`, `fatal`). Ordering is meaningful:
//! the configured `min_level` drops anything below it before an event is ever
//! built, so this enum derives `Ord` and the variants are declared
//! lowest-to-highest.

use serde::{Deserialize, Serialize};
use std::fmt;
use std::str::FromStr;

/// An event's severity. Variants are ordered `Debug < Info < Warning < Error < Fatal`,
/// which is what makes `level >= config.min_level` a correct threshold check.
#[derive(
    Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Serialize, Deserialize,
)]
#[serde(rename_all = "lowercase")]
pub enum Level {
    Debug,
    #[default]
    Info,
    Warning,
    Error,
    Fatal,
}

impl Level {
    /// The lowercase wire string the backend expects (`"warning"`, not `"Warning"`).
    pub const fn as_str(self) -> &'static str {
        match self {
            Level::Debug => "debug",
            Level::Info => "info",
            Level::Warning => "warning",
            Level::Error => "error",
            Level::Fatal => "fatal",
        }
    }
}

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

impl FromStr for Level {
    type Err = ();

    /// Case-insensitive parse. Accepts `warn` as an alias for `warning` so the
    /// `log`/`tracing` level names map cleanly. Unknown strings are rejected so
    /// the caller can decide on a default rather than silently getting `Info`.
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s.trim().to_ascii_lowercase().as_str() {
            "debug" | "trace" => Ok(Level::Debug),
            "info" => Ok(Level::Info),
            "warn" | "warning" => Ok(Level::Warning),
            "error" => Ok(Level::Error),
            "fatal" | "critical" => Ok(Level::Fatal),
            _ => Err(()),
        }
    }
}

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

    #[test]
    fn ordering_is_severity_ordering() {
        assert!(Level::Debug < Level::Info);
        assert!(Level::Info < Level::Warning);
        assert!(Level::Warning < Level::Error);
        assert!(Level::Error < Level::Fatal);
    }

    #[test]
    fn serializes_lowercase() {
        let json = serde_json::to_string(&Level::Warning).unwrap();
        assert_eq!(json, "\"warning\"");
    }

    #[test]
    fn parse_aliases() {
        assert_eq!(Level::from_str("WARN").unwrap(), Level::Warning);
        assert_eq!(Level::from_str("trace").unwrap(), Level::Debug);
        assert_eq!(Level::from_str("critical").unwrap(), Level::Fatal);
        assert!(Level::from_str("nope").is_err());
    }
}