1use crate::capability::{CapabilityType, OperationCapability};
4use serde::{Deserialize, Serialize};
5
6#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
7pub enum CameraPermission {
8 #[default]
9 Unknown,
10 Granted,
11 Denied,
12 Restricted,
13}
14
15#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
16pub enum CameraFacing {
17 Front,
18 Back,
19 External,
20 #[default]
21 Unspecified,
22}
23
24#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
25pub enum CameraImageFormat {
26 #[default]
27 Jpeg,
28 Png,
29 Heif,
30 Raw,
31}
32
33#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
34pub enum CameraFlashMode {
35 #[default]
36 Off,
37 On,
38 Auto,
39}
40
41#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
42pub struct CameraResolution {
43 pub width: u32,
44 pub height: u32,
45}
46
47#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
48pub struct CameraDevice {
49 pub id: String,
50 pub label: Option<String>,
51 pub facing: CameraFacing,
52 pub has_flashlight: bool,
53}
54
55#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
56pub struct CameraAvailability {
57 pub permission: CameraPermission,
58 pub devices: Vec<CameraDevice>,
59}
60
61#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
62pub struct CameraPermissionRequest {
63 pub reason: Option<String>,
64}
65
66#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
67pub struct CameraCaptureRequest {
68 pub camera_id: Option<String>,
69 pub facing: CameraFacing,
70 pub resolution: Option<CameraResolution>,
71 pub format: CameraImageFormat,
72 pub flash: CameraFlashMode,
73 pub quality: Option<u8>,
74}
75
76#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
77pub struct CameraCapture {
78 pub bytes: Vec<u8>,
79 pub content_type: String,
80 pub width: u32,
81 pub height: u32,
82 pub camera_id: Option<String>,
83}
84
85#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
86pub struct CameraFlashlightRequest {
87 pub camera_id: Option<String>,
88 pub enabled: bool,
89 pub intensity: Option<u8>,
90}
91
92#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
93pub struct CameraError {
94 pub code: String,
95 pub message: String,
96}
97
98impl CameraError {
99 pub fn new(code: impl Into<String>, message: impl Into<String>) -> Self {
106 Self {
107 code: code.into(),
108 message: message.into(),
109 }
110 }
111
112 pub fn unsupported(operation: impl Into<String>) -> Self {
118 Self::new(
119 "unsupported",
120 format!(
121 "camera operation `{}` is not supported by this host",
122 operation.into()
123 ),
124 )
125 }
126}
127
128pub struct GetCameraAvailabilityCapability;
129impl OperationCapability for GetCameraAvailabilityCapability {
130 type Request = ();
131 type Ok = CameraAvailability;
132 type Err = CameraError;
133}
134
135pub struct RequestCameraPermissionCapability;
136impl OperationCapability for RequestCameraPermissionCapability {
137 type Request = CameraPermissionRequest;
138 type Ok = CameraPermission;
139 type Err = CameraError;
140}
141
142pub struct CapturePhotoCapability;
143impl OperationCapability for CapturePhotoCapability {
144 type Request = CameraCaptureRequest;
145 type Ok = CameraCapture;
146 type Err = CameraError;
147}
148
149pub struct SetCameraFlashlightCapability;
150impl OperationCapability for SetCameraFlashlightCapability {
151 type Request = CameraFlashlightRequest;
152 type Ok = ();
153 type Err = CameraError;
154}
155
156pub struct CancelCameraCaptureCapability;
157impl OperationCapability for CancelCameraCaptureCapability {
158 type Request = ();
159 type Ok = ();
160 type Err = CameraError;
161}
162
163pub const GET_CAMERA_AVAILABILITY: CapabilityType<GetCameraAvailabilityCapability> =
164 CapabilityType::new("fission.camera.get_availability");
165pub const REQUEST_CAMERA_PERMISSION: CapabilityType<RequestCameraPermissionCapability> =
166 CapabilityType::new("fission.camera.request_permission");
167pub const CAPTURE_PHOTO: CapabilityType<CapturePhotoCapability> =
168 CapabilityType::new("fission.camera.capture_photo");
169pub const SET_CAMERA_FLASHLIGHT: CapabilityType<SetCameraFlashlightCapability> =
170 CapabilityType::new("fission.camera.set_flashlight");
171pub const CANCEL_CAMERA_CAPTURE: CapabilityType<CancelCameraCaptureCapability> =
172 CapabilityType::new("fission.camera.cancel_capture");
173
174#[cfg(test)]
175mod tests {
176 use super::*;
177
178 #[test]
179 fn camera_capture_request_round_trips() {
180 let request = CameraCaptureRequest {
181 camera_id: Some("back".into()),
182 facing: CameraFacing::Back,
183 resolution: Some(CameraResolution {
184 width: 1920,
185 height: 1080,
186 }),
187 format: CameraImageFormat::Heif,
188 flash: CameraFlashMode::Auto,
189 quality: Some(90),
190 };
191
192 let bytes = serde_json::to_vec(&request).unwrap();
193 let decoded: CameraCaptureRequest = serde_json::from_slice(&bytes).unwrap();
194
195 assert_eq!(decoded, request);
196 }
197
198 #[test]
199 fn camera_availability_round_trips() {
200 let availability = CameraAvailability {
201 permission: CameraPermission::Granted,
202 devices: vec![CameraDevice {
203 id: "front".into(),
204 label: Some("Front camera".into()),
205 facing: CameraFacing::Front,
206 has_flashlight: false,
207 }],
208 };
209
210 let bytes = serde_json::to_vec(&availability).unwrap();
211 let decoded: CameraAvailability = serde_json::from_slice(&bytes).unwrap();
212
213 assert_eq!(decoded, availability);
214 }
215}