Skip to main content

safe_chains/
verdict.rs

1use std::fmt;
2
3#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
4#[repr(u8)]
5pub enum SafetyLevel {
6    Inert = 0,
7    SafeRead = 1,
8    SafeWrite = 2,
9}
10
11#[derive(Copy, Clone, Debug, PartialEq, Eq)]
12pub enum Verdict {
13    Denied,
14    Allowed(SafetyLevel),
15}
16
17impl Verdict {
18    pub fn combine(self, other: Verdict) -> Verdict {
19        match (self, other) {
20            (Verdict::Denied, _) | (_, Verdict::Denied) => Verdict::Denied,
21            (Verdict::Allowed(a), Verdict::Allowed(b)) => Verdict::Allowed(a.max(b)),
22        }
23    }
24
25    pub fn is_allowed(self) -> bool {
26        matches!(self, Verdict::Allowed(_))
27    }
28}
29
30impl fmt::Display for SafetyLevel {
31    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32        match self {
33            SafetyLevel::Inert => write!(f, "inert"),
34            SafetyLevel::SafeRead => write!(f, "safe-read"),
35            SafetyLevel::SafeWrite => write!(f, "safe-write"),
36        }
37    }
38}
39
40impl fmt::Display for Verdict {
41    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42        match self {
43            Verdict::Denied => write!(f, "denied"),
44            Verdict::Allowed(level) => write!(f, "allowed ({level})"),
45        }
46    }
47}
48
49impl clap::ValueEnum for SafetyLevel {
50    fn value_variants<'a>() -> &'a [Self] {
51        &[SafetyLevel::Inert, SafetyLevel::SafeRead, SafetyLevel::SafeWrite]
52    }
53
54    fn to_possible_value(&self) -> Option<clap::builder::PossibleValue> {
55        match self {
56            SafetyLevel::Inert => Some(clap::builder::PossibleValue::new("inert")),
57            SafetyLevel::SafeRead => Some(clap::builder::PossibleValue::new("safe-read")),
58            SafetyLevel::SafeWrite => Some(clap::builder::PossibleValue::new("safe-write")),
59        }
60    }
61}
62
63#[cfg(test)]
64mod tests {
65    use super::*;
66
67    #[test]
68    fn level_ordering() {
69        assert!(SafetyLevel::Inert < SafetyLevel::SafeRead);
70        assert!(SafetyLevel::SafeRead < SafetyLevel::SafeWrite);
71    }
72
73    #[test]
74    fn combine_both_allowed() {
75        let a = Verdict::Allowed(SafetyLevel::Inert);
76        let b = Verdict::Allowed(SafetyLevel::SafeRead);
77        assert_eq!(a.combine(b), Verdict::Allowed(SafetyLevel::SafeRead));
78    }
79
80    #[test]
81    fn combine_one_denied() {
82        let a = Verdict::Allowed(SafetyLevel::Inert);
83        assert_eq!(a.combine(Verdict::Denied), Verdict::Denied);
84        assert_eq!(Verdict::Denied.combine(a), Verdict::Denied);
85    }
86
87    #[test]
88    fn combine_identity() {
89        let a = Verdict::Allowed(SafetyLevel::SafeWrite);
90        let identity = Verdict::Allowed(SafetyLevel::Inert);
91        assert_eq!(identity.combine(a), a);
92    }
93}