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