use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Severity {
Error,
Warning,
Info,
Debug,
}
impl Severity {
pub fn word(self) -> &'static str {
match self {
Severity::Error => "error",
Severity::Warning => "warning",
Severity::Info => "info",
Severity::Debug => "debug",
}
}
pub fn shown_at(self, level: Severity) -> bool {
self <= level
}
}
impl fmt::Display for Severity {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.word())
}
}
impl std::str::FromStr for Severity {
type Err = String;
fn from_str(s: &str) -> Result<Severity, String> {
match s.trim().to_ascii_lowercase().as_str() {
"error" | "errors" => Ok(Severity::Error),
"warning" | "warnings" | "warn" => Ok(Severity::Warning),
"info" | "infos" => Ok(Severity::Info),
"debug" | "all" => Ok(Severity::Debug),
other => Err(format!(
"'{other}' is no diagnostics level: error, warning, info or debug"
)),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Diagnostic {
pub severity: Severity,
pub code: String,
pub message: String,
pub site: String,
}
impl Diagnostic {
pub fn new(
severity: Severity,
code: impl Into<String>,
site: impl Into<String>,
message: impl Into<String>,
) -> Diagnostic {
Diagnostic {
severity,
code: code.into(),
message: message.into(),
site: site.into(),
}
}
pub fn error(
code: impl Into<String>,
site: impl Into<String>,
message: impl Into<String>,
) -> Diagnostic {
Diagnostic::new(Severity::Error, code, site, message)
}
pub fn warning(
code: impl Into<String>,
site: impl Into<String>,
message: impl Into<String>,
) -> Diagnostic {
Diagnostic::new(Severity::Warning, code, site, message)
}
pub fn info(
code: impl Into<String>,
site: impl Into<String>,
message: impl Into<String>,
) -> Diagnostic {
Diagnostic::new(Severity::Info, code, site, message)
}
pub fn line(&self) -> String {
format!(
"{} {} ({}): {}",
self.severity, self.code, self.site, self.message
)
}
}
impl fmt::Display for Diagnostic {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.line())
}
}
pub const REFUSED: &str = "REFUSED";
pub const OMITTED: &str = "OMITTED";
pub const OMITTED_TEXT: &str = "some data is omitted due to insufficient permissions";
pub const SYNTAX: &str = "SYNTAX";
pub const QUANTIFIER_BOUND: &str = "QUANTIFIER_BOUND";
pub const FRONTIER_BOUND: &str = "FRONTIER_BOUND";
pub const WALK: &str = "WALK";
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn levels_and_lines() {
assert!(Severity::Error.shown_at(Severity::Error));
assert!(!Severity::Warning.shown_at(Severity::Error));
assert!(Severity::Warning.shown_at(Severity::Warning));
assert!(Severity::Info.shown_at(Severity::Info));
assert!(!Severity::Info.shown_at(Severity::Warning));
assert!(Severity::Debug.shown_at(Severity::Debug));
assert!(!Severity::Debug.shown_at(Severity::Info));
assert_eq!("debug".parse::<Severity>().unwrap(), Severity::Debug);
assert_eq!("warn".parse::<Severity>().unwrap(), Severity::Warning);
assert!("loud".parse::<Severity>().is_err());
let d = Diagnostic::error(QUANTIFIER_BOUND, "round 32", "edges still to take");
assert_eq!(
d.to_string(),
"error QUANTIFIER_BOUND (round 32): edges still to take"
);
}
}