use std::sync::Arc;
use std::sync::Mutex;
use std::sync::OnceLock;
#[derive(Clone)]
pub struct CameraFrame {
pub width: u32,
pub height: u32,
pub rgba: Vec<u8>,
}
#[derive(Debug, thiserror::Error)]
pub enum CameraError {
#[error("live camera capture is not supported here")]
Unsupported,
#[error("camera permission denied")]
PermissionDenied,
#[error("{0}")]
Failed(String),
}
pub trait Camera: Send + Sync {
fn start(&self) -> Result<String, CameraError>;
fn latest_frame(&self) -> Option<CameraFrame>;
fn stop(&self);
}
pub type CameraRef = Arc<dyn Camera>;
fn slot() -> &'static Mutex<Option<CameraRef>> {
static SLOT: OnceLock<Mutex<Option<CameraRef>>> = OnceLock::new();
SLOT.get_or_init(|| Mutex::new(None))
}
pub fn set_platform_camera(camera: CameraRef) {
if let Ok(mut s) = slot().lock() {
*s = Some(camera);
}
}
pub fn clear_platform_camera() {
if let Ok(mut s) = slot().lock() {
*s = None;
}
}
pub fn camera() -> Option<CameraRef> {
slot().lock().ok().and_then(|s| s.clone())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn registration_round_trips() {
clear_platform_camera();
assert!(camera().is_none());
struct Fake;
impl Camera for Fake {
fn start(&self) -> Result<String, CameraError> {
Ok("fake".into())
}
fn latest_frame(&self) -> Option<CameraFrame> {
Some(CameraFrame {
width: 1,
height: 1,
rgba: vec![0, 0, 0, 255],
})
}
fn stop(&self) {}
}
set_platform_camera(Arc::new(Fake));
let cam = camera().expect("registered");
assert_eq!(cam.start().unwrap(), "fake");
assert_eq!(cam.latest_frame().unwrap().width, 1);
clear_platform_camera();
}
}