1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
use std::any::TypeId;
use std::convert::TryFrom;
use std::fmt::{Debug, Display};
use crate::{BasicDiag, Diag};

#[derive(Debug, Display, Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub enum Severity {
    #[display("info")]
    Info,

    #[display("warning")]
    Warning,

    /// error that is recoverable (i.e. process might continue and check additional diagnostics)
    #[display("error")]
    Error,

    /// non-recoverable error
    #[display("error")]
    Failure,

    /// fatal error, usually terminating the process abnormally (like OOM errors)
    #[display("critical error")]
    Critical,
}

impl Severity {
    pub fn code_byte(&self) -> u8 {
        match *self {
            Severity::Info => b'I',
            Severity::Warning => b'W',
            Severity::Error => b'E',
            Severity::Failure => b'F',
            Severity::Critical => b'C',
        }
    }

    pub fn code_char(&self) -> char {
        self.code_byte() as char
    }

    pub fn is_error(&self) -> bool {
        *self >= Severity::Error
    }

    pub fn is_recoverable(&self) -> bool {
        *self < Severity::Failure
    }
}

impl<'a> TryFrom<&'a str> for Severity {
    type Error = &'a str;

    fn try_from(value: &'a str) -> Result<Self, <Self as TryFrom<&'a str>>::Error> {
        if value.eq_ignore_ascii_case("info") {
            Ok(Severity::Info)
        } else if value.eq_ignore_ascii_case("warning") {
            Ok(Severity::Warning)
        } else if value.eq_ignore_ascii_case("error") {
            Ok(Severity::Error)
        } else if value.eq_ignore_ascii_case("failure") {
            Ok(Severity::Failure)
        } else if value.eq_ignore_ascii_case("critical") {
            Ok(Severity::Critical)
        } else {
            Err(value)
        }
    }
}

impl TryFrom<char> for Severity {
    type Error = char;

    fn try_from(value: char) -> Result<Self, <Self as TryFrom<char>>::Error> {
        Ok(match value.to_ascii_uppercase() {
            'I' => Severity::Info,
            'W' => Severity::Warning,
            'E' => Severity::Error,
            'F' => Severity::Failure,
            'C' => Severity::Critical,
            _ => return Err(value),
        })
    }
}

pub trait Detail: Display + Debug + Send + Sync + 'static {
    fn severity(&self) -> Severity {
        Severity::Failure
    }

    fn code(&self) -> u32 {
        0
    }

    fn type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }

    fn as_fmt_debug(&self) -> &dyn std::fmt::Debug;

    fn as_fmt_display(&self) -> &dyn std::fmt::Display;
}

default impl<T: Detail> Detail for T {
    fn as_fmt_debug(&self) -> &dyn std::fmt::Debug {
        self as &dyn std::fmt::Debug
    }

    fn as_fmt_display(&self) -> &dyn std::fmt::Display {
        self as &dyn std::fmt::Display
    }
}

impl dyn Detail {
    pub fn downcast_ref<T: Detail>(&self) -> Option<&T> {
        if self.type_id() == TypeId::of::<T>() {
            unsafe { Some(&*(self as *const dyn Detail as *const T)) }
        } else {
            None
        }
    }

    pub fn downcast_mut<T: Detail>(&mut self) -> Option<&mut T> {
        if self.type_id() == TypeId::of::<T>() {
            unsafe { Some(&mut *(self as *mut dyn Detail as *mut T)) }
        } else {
            None
        }
    }
}

pub trait DetailExt {
    fn with_cause<D: Diag>(self, cause: D) -> BasicDiag;
}

impl <T> DetailExt for T
    where T: Detail {
    fn with_cause<D: Diag>(self, cause: D) -> BasicDiag {
       BasicDiag::with_cause(self, cause)
    }
}