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