use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
pub type PlatformPermissionRequirements = BTreeMap<String, Vec<PermissionRequirement>>;
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(tag = "id", rename_all = "camelCase", deny_unknown_fields)]
pub enum PermissionKey {
LocalNetwork {},
Bluetooth {},
Microphone {},
Reminders {},
Automation {
#[serde(rename = "targetBundleId")]
target_bundle_id: String,
},
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct PermissionRequirement {
pub permission: PermissionKey,
pub feature: String,
pub reason: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum PermissionState {
Unknown,
NotDetermined,
Granted,
NotRequired,
Denied,
Restricted,
Unsupported,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum PermissionEvidence {
System,
Platform,
Probe,
Unavailable,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum PermissionAction {
Request,
OpenSettings,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct PermissionObservation {
pub permission: PermissionKey,
pub process_id: u32,
pub state: PermissionState,
pub evidence: PermissionEvidence,
pub observed_at_ms: Option<i64>,
pub error: Option<String>,
}
impl PermissionObservation {
pub fn is_satisfied(&self) -> bool {
self.process_id != 0
&& self.error.is_none()
&& self.evidence != PermissionEvidence::Unavailable
&& (self.evidence != PermissionEvidence::Probe || self.observed_at_ms.is_some())
&& matches!(
self.state,
PermissionState::Granted | PermissionState::NotRequired
)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct ServicePermissions {
pub process_id: u32,
pub permissions: Vec<PermissionObservation>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct HostPermission {
pub component_id: String,
pub component_name: String,
pub permission: PermissionKey,
pub feature: String,
pub reason: String,
pub observation: Option<PermissionObservation>,
pub error: Option<String>,
pub supported_actions: Vec<PermissionAction>,
}
impl HostPermission {
pub fn is_satisfied(&self) -> bool {
self.error.is_none()
&& self.observation.as_ref().is_some_and(|observation| {
observation.permission == self.permission && observation.is_satisfied()
})
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct PermissionDeclarationError {
pub component_id: String,
pub message: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct HostPermissions {
pub host_id: String,
pub host_version: String,
pub platform: String,
pub observed_at_ms: i64,
pub permissions: Vec<HostPermission>,
pub declaration_errors: Vec<PermissionDeclarationError>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct HostPermissionRequest {
pub host_id: String,
pub component_id: String,
pub permission: PermissionKey,
}
pub const HOST_PERMISSIONS_PATH: &str = "/v1/permissions";
pub const HOST_PERMISSION_REQUEST_PATH: &str = "/internal/permissions/request";
pub const HOST_PERMISSION_SETTINGS_PATH: &str = "/internal/permissions/open-settings";