1use std::fmt;
8use std::str::FromStr;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
12pub enum Kind {
13 Shell,
15 FsRead,
17 FsWrite,
19 Net,
21 Fetch,
24 EnvRead,
26 EnvSet,
28 Mcp,
30}
31
32impl Kind {
33 pub const ALL: [Self; 8] = [
35 Self::Shell,
36 Self::FsRead,
37 Self::FsWrite,
38 Self::Net,
39 Self::Fetch,
40 Self::EnvRead,
41 Self::EnvSet,
42 Self::Mcp,
43 ];
44
45 #[must_use]
47 pub fn as_str(self) -> &'static str {
48 match self {
49 Self::Shell => "shell",
50 Self::FsRead => "fs.read",
51 Self::FsWrite => "fs.write",
52 Self::Net => "net",
53 Self::Fetch => "fetch",
54 Self::EnvRead => "env.read",
55 Self::EnvSet => "env.set",
56 Self::Mcp => "mcp",
57 }
58 }
59
60 #[must_use]
64 pub fn rule_kinds(self) -> &'static [Self] {
65 match self {
66 Self::Fetch => &[Self::Fetch, Self::Net],
67 Self::Shell => &[Self::Shell],
68 Self::FsRead => &[Self::FsRead],
69 Self::FsWrite => &[Self::FsWrite],
70 Self::Net => &[Self::Net],
71 Self::EnvRead => &[Self::EnvRead],
72 Self::EnvSet => &[Self::EnvSet],
73 Self::Mcp => &[Self::Mcp],
74 }
75 }
76}
77
78impl fmt::Display for Kind {
79 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80 f.write_str(self.as_str())
81 }
82}
83
84impl serde::Serialize for Kind {
86 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
87 serializer.serialize_str(self.as_str())
88 }
89}
90
91#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
93#[error("unknown action kind `{0}`")]
94pub struct UnknownKind(pub String);
95
96impl FromStr for Kind {
97 type Err = UnknownKind;
98
99 fn from_str(s: &str) -> Result<Self, Self::Err> {
100 Self::ALL
101 .into_iter()
102 .find(|k| k.as_str() == s)
103 .ok_or_else(|| UnknownKind(s.to_owned()))
104 }
105}
106
107#[cfg(test)]
108mod tests {
109 use super::*;
110
111 #[test]
112 fn names_round_trip() {
113 for kind in Kind::ALL {
114 assert_eq!(kind.as_str().parse::<Kind>(), Ok(kind));
115 }
116 assert_eq!(
117 "network".parse::<Kind>(),
118 Err(UnknownKind("network".into()))
119 );
120 }
121
122 #[test]
123 fn only_fetch_borrows_another_kinds_rules() {
124 assert_eq!(Kind::Fetch.rule_kinds(), &[Kind::Fetch, Kind::Net]);
125 for kind in Kind::ALL.into_iter().filter(|k| *k != Kind::Fetch) {
126 assert_eq!(kind.rule_kinds(), &[kind]);
127 }
128 }
129}