Skip to main content

zeph_tools/
trust_level.rs

1// SPDX-FileCopyrightText: 2026 Andrei G <bug-ops>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4//! Trust tier enum for skill execution permissions.
5
6use std::fmt;
7
8use serde::{Deserialize, Serialize};
9
10/// Trust tier controlling what a skill is allowed to do.
11#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, Deserialize, Serialize)]
12#[serde(rename_all = "lowercase")]
13pub enum TrustLevel {
14    /// Built-in or user-audited skill: full tool access.
15    Trusted,
16    /// Signature or hash verified: default tool access.
17    Verified,
18    /// Newly imported or hash-mismatch: restricted tool access.
19    #[default]
20    Quarantined,
21    /// Explicitly disabled by user or auto-blocked by anomaly detector.
22    Blocked,
23}
24
25impl TrustLevel {
26    /// Ordered severity: lower value = more trusted.
27    #[must_use]
28    pub fn severity(self) -> u8 {
29        match self {
30            Self::Trusted => 0,
31            Self::Verified => 1,
32            Self::Quarantined => 2,
33            Self::Blocked => 3,
34        }
35    }
36
37    /// Returns the least-trusted (highest severity) of two levels.
38    #[must_use]
39    pub fn min_trust(self, other: Self) -> Self {
40        if self.severity() >= other.severity() {
41            self
42        } else {
43            other
44        }
45    }
46
47    #[must_use]
48    pub fn is_active(self) -> bool {
49        !matches!(self, Self::Blocked)
50    }
51}
52
53impl fmt::Display for TrustLevel {
54    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55        match self {
56            Self::Trusted => f.write_str("trusted"),
57            Self::Verified => f.write_str("verified"),
58            Self::Quarantined => f.write_str("quarantined"),
59            Self::Blocked => f.write_str("blocked"),
60        }
61    }
62}
63
64#[cfg(test)]
65mod tests {
66    use super::*;
67
68    #[test]
69    fn severity_ordering() {
70        assert!(TrustLevel::Trusted.severity() < TrustLevel::Verified.severity());
71        assert!(TrustLevel::Verified.severity() < TrustLevel::Quarantined.severity());
72        assert!(TrustLevel::Quarantined.severity() < TrustLevel::Blocked.severity());
73    }
74
75    #[test]
76    fn min_trust_picks_least_trusted() {
77        assert_eq!(
78            TrustLevel::Trusted.min_trust(TrustLevel::Quarantined),
79            TrustLevel::Quarantined
80        );
81        assert_eq!(
82            TrustLevel::Blocked.min_trust(TrustLevel::Trusted),
83            TrustLevel::Blocked
84        );
85    }
86
87    #[test]
88    fn is_active() {
89        assert!(TrustLevel::Trusted.is_active());
90        assert!(TrustLevel::Verified.is_active());
91        assert!(TrustLevel::Quarantined.is_active());
92        assert!(!TrustLevel::Blocked.is_active());
93    }
94
95    #[test]
96    fn default_is_quarantined() {
97        assert_eq!(TrustLevel::default(), TrustLevel::Quarantined);
98    }
99
100    #[test]
101    fn display() {
102        assert_eq!(TrustLevel::Trusted.to_string(), "trusted");
103        assert_eq!(TrustLevel::Blocked.to_string(), "blocked");
104        assert_eq!(TrustLevel::Quarantined.to_string(), "quarantined");
105        assert_eq!(TrustLevel::Verified.to_string(), "verified");
106    }
107
108    #[test]
109    fn serde_roundtrip() {
110        let level = TrustLevel::Quarantined;
111        let json = serde_json::to_string(&level).unwrap();
112        assert_eq!(json, "\"quarantined\"");
113        let back: TrustLevel = serde_json::from_str(&json).unwrap();
114        assert_eq!(back, level);
115    }
116
117    #[test]
118    fn serde_all_variants() {
119        let cases = [
120            (TrustLevel::Trusted, "\"trusted\""),
121            (TrustLevel::Verified, "\"verified\""),
122            (TrustLevel::Quarantined, "\"quarantined\""),
123            (TrustLevel::Blocked, "\"blocked\""),
124        ];
125        for (level, expected_json) in cases {
126            let json = serde_json::to_string(&level).unwrap();
127            assert_eq!(json, expected_json);
128            let back: TrustLevel = serde_json::from_str(&json).unwrap();
129            assert_eq!(back, level);
130        }
131    }
132
133    #[test]
134    fn min_trust_same_level_returns_self() {
135        assert_eq!(
136            TrustLevel::Verified.min_trust(TrustLevel::Verified),
137            TrustLevel::Verified
138        );
139        assert_eq!(
140            TrustLevel::Blocked.min_trust(TrustLevel::Blocked),
141            TrustLevel::Blocked
142        );
143    }
144
145    #[test]
146    fn hash_consistent() {
147        use std::collections::HashSet;
148        let mut set = HashSet::new();
149        set.insert(TrustLevel::Trusted);
150        set.insert(TrustLevel::Verified);
151        set.insert(TrustLevel::Quarantined);
152        set.insert(TrustLevel::Blocked);
153        assert_eq!(set.len(), 4);
154        // Inserting same value again does not grow the set
155        set.insert(TrustLevel::Trusted);
156        assert_eq!(set.len(), 4);
157    }
158}