use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum Capability {
Capture,
InputMouse,
InputKeyboard,
InputGamepad,
FsRead { path: String },
Net { host: String },
Bridge { name: String },
}
impl Capability {
pub fn label(&self) -> String {
match self {
Capability::Capture => "capture".into(),
Capability::InputMouse => "input.mouse".into(),
Capability::InputKeyboard => "input.keyboard".into(),
Capability::InputGamepad => "input.gamepad".into(),
Capability::FsRead { path } => format!("fs.read:{path}"),
Capability::Net { host } => format!("net:{host}"),
Capability::Bridge { name } => format!("bridge:{name}"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum TrustLevel {
Unverified,
Verified,
Official,
}
impl TrustLevel {
pub fn allows_auto_update(self) -> bool {
self >= TrustLevel::Verified
}
pub fn grants_silently(self, cap: &Capability) -> bool {
if matches!(cap, Capability::Bridge { .. }) {
return false;
}
match self {
TrustLevel::Official => true,
TrustLevel::Verified => !matches!(cap, Capability::Net { .. }),
TrustLevel::Unverified => false,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
const LEVELS: [TrustLevel; 3] = [
TrustLevel::Unverified,
TrustLevel::Verified,
TrustLevel::Official,
];
#[test]
fn a_bridge_is_never_granted_silently() {
let bridge = Capability::Bridge {
name: "cookie".into(),
};
for level in LEVELS {
assert!(
!level.grants_silently(&bridge),
"{level:?} granted a bridge silently"
);
}
}
#[test]
fn an_official_plugin_still_gets_everything_else_silently() {
assert!(TrustLevel::Official.grants_silently(&Capability::Capture));
assert!(TrustLevel::Official.grants_silently(&Capability::Net { host: "x".into() }));
}
#[test]
fn capability_labels_round_trip_into_the_registry_pattern() {
assert_eq!(
Capability::Bridge {
name: "cookie".into()
}
.label(),
"bridge:cookie"
);
assert_eq!(Capability::InputMouse.label(), "input.mouse");
}
}