1use serde::{Deserialize, Serialize};
10use std::collections::BTreeMap;
11
12pub type PlatformPermissionRequirements = BTreeMap<String, Vec<PermissionRequirement>>;
13
14#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
16#[serde(tag = "id", rename_all = "camelCase", deny_unknown_fields)]
17pub enum PermissionKey {
18 LocalNetwork {},
19 Bluetooth {},
20 Microphone {},
21 Reminders {},
22 Automation {
23 #[serde(rename = "targetBundleId")]
24 target_bundle_id: String,
25 },
26}
27
28#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
29#[serde(rename_all = "camelCase", deny_unknown_fields)]
30pub struct PermissionRequirement {
31 pub permission: PermissionKey,
32 pub feature: String,
34 pub reason: String,
35}
36
37#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
38#[serde(rename_all = "camelCase")]
39pub enum PermissionState {
40 Unknown,
41 NotDetermined,
42 Granted,
43 NotRequired,
46 Denied,
47 Restricted,
48 Unsupported,
49}
50
51#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
52#[serde(rename_all = "camelCase")]
53pub enum PermissionEvidence {
54 System,
56 Platform,
59 Probe,
61 Unavailable,
62}
63
64#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
65#[serde(rename_all = "camelCase")]
66pub enum PermissionAction {
67 Request,
68 OpenSettings,
69}
70
71#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
72#[serde(rename_all = "camelCase", deny_unknown_fields)]
73pub struct PermissionObservation {
74 pub permission: PermissionKey,
75 pub process_id: u32,
77 pub state: PermissionState,
78 pub evidence: PermissionEvidence,
79 pub observed_at_ms: Option<i64>,
80 pub error: Option<String>,
81}
82
83impl PermissionObservation {
84 pub fn is_satisfied(&self) -> bool {
85 self.process_id != 0
86 && self.error.is_none()
87 && self.evidence != PermissionEvidence::Unavailable
88 && (self.evidence != PermissionEvidence::Probe || self.observed_at_ms.is_some())
89 && matches!(
90 self.state,
91 PermissionState::Granted | PermissionState::NotRequired
92 )
93 }
94}
95
96#[derive(Debug, Clone, Serialize, Deserialize)]
97#[serde(rename_all = "camelCase", deny_unknown_fields)]
98pub struct ServicePermissions {
99 pub process_id: u32,
101 pub permissions: Vec<PermissionObservation>,
102}
103
104#[derive(Debug, Clone, Serialize, Deserialize)]
105#[serde(rename_all = "camelCase", deny_unknown_fields)]
106pub struct HostPermission {
107 pub component_id: String,
108 pub component_name: String,
109 pub permission: PermissionKey,
110 pub feature: String,
111 pub reason: String,
112 pub observation: Option<PermissionObservation>,
114 pub error: Option<String>,
116 pub supported_actions: Vec<PermissionAction>,
118}
119
120impl HostPermission {
121 pub fn is_satisfied(&self) -> bool {
122 self.error.is_none()
123 && self.observation.as_ref().is_some_and(|observation| {
124 observation.permission == self.permission && observation.is_satisfied()
125 })
126 }
127}
128
129#[derive(Debug, Clone, Serialize, Deserialize)]
130#[serde(rename_all = "camelCase", deny_unknown_fields)]
131pub struct PermissionDeclarationError {
132 pub component_id: String,
133 pub message: String,
134}
135
136#[derive(Debug, Clone, Serialize, Deserialize)]
137#[serde(rename_all = "camelCase", deny_unknown_fields)]
138pub struct HostPermissions {
139 pub host_id: String,
140 pub host_version: String,
141 pub platform: String,
142 pub observed_at_ms: i64,
143 pub permissions: Vec<HostPermission>,
144 pub declaration_errors: Vec<PermissionDeclarationError>,
147}
148
149#[derive(Debug, Clone, Serialize, Deserialize)]
156#[serde(rename_all = "camelCase", deny_unknown_fields)]
157pub struct HostPermissionRequest {
158 pub host_id: String,
159 pub component_id: String,
160 pub permission: PermissionKey,
161}
162
163pub const HOST_PERMISSIONS_PATH: &str = "/v1/permissions";
164pub const HOST_PERMISSION_REQUEST_PATH: &str = "/internal/permissions/request";
165pub const HOST_PERMISSION_SETTINGS_PATH: &str = "/internal/permissions/open-settings";