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}