#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
#[serde(tag = "type", content = "value", rename_all = "snake_case")]
pub enum Permission {
DatabaseReadAll,
DatabaseRead(String),
DatabaseWrite(String),
DatabaseCreateTables,
FilesystemRead,
FilesystemReadAppData,
FilesystemWrite,
FilesystemWriteAppData,
NetworkHttp,
NetworkHttpDomain(String),
IpcRegister,
EventsEmit,
EventsListen,
UiInject,
ProcessSpawn,
ProcessSpawnWhitelist(Vec<String>),
Notifications,
ClipboardRead,
ClipboardWrite,
HostNavigation,
HostFileIntents,
HostAIChatAccess,
HostThemeRead,
HostEventSubscribe,
AppConfigRead,
}
impl Permission {
pub fn tier(&self) -> PermissionTier {
match self {
Self::UiInject
| Self::EventsListen
| Self::DatabaseCreateTables
| Self::AppConfigRead
| Self::Notifications
| Self::HostThemeRead => PermissionTier::Transparent,
Self::DatabaseReadAll
| Self::DatabaseRead(_)
| Self::IpcRegister
| Self::EventsEmit
| Self::NetworkHttpDomain(_)
| Self::HostNavigation
| Self::HostFileIntents
| Self::HostAIChatAccess
| Self::HostEventSubscribe => PermissionTier::Standard,
Self::FilesystemRead
| Self::FilesystemWrite
| Self::FilesystemReadAppData
| Self::FilesystemWriteAppData
| Self::NetworkHttp
| Self::ProcessSpawnWhitelist(_)
| Self::ClipboardRead
| Self::ClipboardWrite => PermissionTier::Sensitive,
Self::DatabaseWrite(_)
| Self::ProcessSpawn => PermissionTier::Restricted,
}
}
pub fn description(&self) -> String {
match self {
Self::DatabaseReadAll => "Read all app data".into(),
Self::DatabaseRead(t) => format!("Read table: {t}"),
Self::DatabaseWrite(t) => format!("Write to table: {t}"),
Self::DatabaseCreateTables => "Create plugin-owned database tables".into(),
Self::FilesystemRead => "Read files from your filesystem".into(),
Self::FilesystemReadAppData => "Read files in the app data directory".into(),
Self::FilesystemWrite => "Write files to your filesystem".into(),
Self::FilesystemWriteAppData => "Write files in the app data directory".into(),
Self::NetworkHttp => "Make outbound HTTP requests".into(),
Self::NetworkHttpDomain(d) => format!("Make HTTP requests to: {d}"),
Self::IpcRegister => "Register new app commands".into(),
Self::EventsEmit => "Emit app events".into(),
Self::EventsListen => "Listen to app lifecycle events".into(),
Self::UiInject => "Inject UI components".into(),
Self::ProcessSpawn => "Spawn arbitrary child processes".into(),
Self::ProcessSpawnWhitelist(v) => format!("Spawn processes: {}", v.join(", ")),
Self::Notifications => "Show desktop notifications".into(),
Self::ClipboardRead => "Read the clipboard".into(),
Self::ClipboardWrite => "Write to the clipboard".into(),
Self::HostNavigation => "Navigate within HaloForge".into(),
Self::HostFileIntents => "Receive file-open intents from HaloForge".into(),
Self::HostAIChatAccess => "Use HaloForge AI models and chat transport".into(),
Self::HostThemeRead => "Read HaloForge theme tokens".into(),
Self::HostEventSubscribe => "Subscribe to HaloForge host events".into(),
Self::AppConfigRead => "Read app configuration".into(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum PermissionTier {
Transparent = 0,
Standard = 1,
Sensitive = 2,
Restricted = 3,
}
#[cfg(test)]
mod tests {
use super::{Permission, PermissionTier};
#[test]
fn host_permissions_have_expected_tiers() {
assert_eq!(Permission::HostThemeRead.tier(), PermissionTier::Transparent);
assert_eq!(Permission::HostNavigation.tier(), PermissionTier::Standard);
assert_eq!(Permission::HostAIChatAccess.tier(), PermissionTier::Standard);
}
}