idlewarden_plugin_api/
capability.rs1use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
10#[serde(tag = "kind", rename_all = "snake_case")]
11pub enum Capability {
12 Capture,
14 InputMouse,
16 InputKeyboard,
18 InputGamepad,
20 FsRead { path: String },
22 Net { host: String },
24 Bridge { name: String },
27}
28
29impl Capability {
30 pub fn label(&self) -> String {
31 match self {
32 Capability::Capture => "capture".into(),
33 Capability::InputMouse => "input.mouse".into(),
34 Capability::InputKeyboard => "input.keyboard".into(),
35 Capability::InputGamepad => "input.gamepad".into(),
36 Capability::FsRead { path } => format!("fs.read:{path}"),
37 Capability::Net { host } => format!("net:{host}"),
38 Capability::Bridge { name } => format!("bridge:{name}"),
39 }
40 }
41}
42
43#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
46#[serde(rename_all = "snake_case")]
47pub enum TrustLevel {
48 Unverified,
51 Verified,
53 Official,
55}
56
57impl TrustLevel {
58 pub fn allows_auto_update(self) -> bool {
59 self >= TrustLevel::Verified
60 }
61
62 pub fn grants_silently(self, cap: &Capability) -> bool {
67 if matches!(cap, Capability::Bridge { .. }) {
68 return false;
69 }
70 match self {
71 TrustLevel::Official => true,
72 TrustLevel::Verified => !matches!(cap, Capability::Net { .. }),
73 TrustLevel::Unverified => false,
74 }
75 }
76}
77
78#[cfg(test)]
79mod tests {
80 use super::*;
81
82 const LEVELS: [TrustLevel; 3] = [
83 TrustLevel::Unverified,
84 TrustLevel::Verified,
85 TrustLevel::Official,
86 ];
87
88 #[test]
89 fn a_bridge_is_never_granted_silently() {
90 let bridge = Capability::Bridge {
91 name: "cookie".into(),
92 };
93 for level in LEVELS {
94 assert!(
95 !level.grants_silently(&bridge),
96 "{level:?} granted a bridge silently"
97 );
98 }
99 }
100
101 #[test]
102 fn an_official_plugin_still_gets_everything_else_silently() {
103 assert!(TrustLevel::Official.grants_silently(&Capability::Capture));
104 assert!(TrustLevel::Official.grants_silently(&Capability::Net { host: "x".into() }));
105 }
106
107 #[test]
108 fn capability_labels_round_trip_into_the_registry_pattern() {
109 assert_eq!(
110 Capability::Bridge {
111 name: "cookie".into()
112 }
113 .label(),
114 "bridge:cookie"
115 );
116 assert_eq!(Capability::InputMouse.label(), "input.mouse");
117 }
118}