crabcamera 0.8.3

Advanced cross-platform camera integration for Tauri applications
Documentation
use crate::headless::errors::HeadlessError;
use crate::types::WhiteBalance;
use std::str::FromStr;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize)]
pub enum ControlId {
    AutoFocus,
    FocusDistance,
    AutoExposure,
    ExposureTime,
    IsoSensitivity,
    WhiteBalance,
    Aperture,
    Zoom,
    Brightness,
    Contrast,
    Saturation,
    Sharpness,
    NoiseReduction,
    ImageStabilization,
}

impl FromStr for ControlId {
    type Err = ();

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "AutoFocus" => Ok(Self::AutoFocus),
            "FocusDistance" => Ok(Self::FocusDistance),
            "AutoExposure" => Ok(Self::AutoExposure),
            "ExposureTime" => Ok(Self::ExposureTime),
            "IsoSensitivity" => Ok(Self::IsoSensitivity),
            "WhiteBalance" => Ok(Self::WhiteBalance),
            "Aperture" => Ok(Self::Aperture),
            "Zoom" => Ok(Self::Zoom),
            "Brightness" => Ok(Self::Brightness),
            "Contrast" => Ok(Self::Contrast),
            "Saturation" => Ok(Self::Saturation),
            "Sharpness" => Ok(Self::Sharpness),
            "NoiseReduction" => Ok(Self::NoiseReduction),
            "ImageStabilization" => Ok(Self::ImageStabilization),
            _ => Err(()),
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize)]
pub enum ControlKind {
    Bool,
    F32,
    U32,
    WhiteBalance,
}

#[derive(Debug, Clone, serde::Serialize)]
pub enum ControlValue {
    Bool(bool),
    F32(f32),
    U32(u32),
    WhiteBalance(WhiteBalance),
}

#[derive(Debug, Clone, serde::Serialize)]
pub struct ControlInfo {
    pub id: ControlId,
    pub kind: ControlKind,
    pub min_f32: Option<f32>,
    pub max_f32: Option<f32>,
    pub min_u32: Option<u32>,
    pub max_u32: Option<u32>,
}

pub fn all_controls() -> Vec<ControlInfo> {
    vec![
        ControlInfo {
            id: ControlId::AutoFocus,
            kind: ControlKind::Bool,
            min_f32: None,
            max_f32: None,
            min_u32: None,
            max_u32: None,
        },
        ControlInfo {
            id: ControlId::FocusDistance,
            kind: ControlKind::F32,
            min_f32: Some(0.0),
            max_f32: Some(1.0),
            min_u32: None,
            max_u32: None,
        },
        ControlInfo {
            id: ControlId::AutoExposure,
            kind: ControlKind::Bool,
            min_f32: None,
            max_f32: None,
            min_u32: None,
            max_u32: None,
        },
        ControlInfo {
            id: ControlId::ExposureTime,
            kind: ControlKind::F32,
            min_f32: Some(0.0),
            max_f32: None,
            min_u32: None,
            max_u32: None,
        },
        ControlInfo {
            id: ControlId::IsoSensitivity,
            kind: ControlKind::U32,
            min_f32: None,
            max_f32: None,
            min_u32: Some(0),
            max_u32: None,
        },
        ControlInfo {
            id: ControlId::WhiteBalance,
            kind: ControlKind::WhiteBalance,
            min_f32: None,
            max_f32: None,
            min_u32: None,
            max_u32: None,
        },
        ControlInfo {
            id: ControlId::Aperture,
            kind: ControlKind::F32,
            min_f32: Some(0.0),
            max_f32: None,
            min_u32: None,
            max_u32: None,
        },
        ControlInfo {
            id: ControlId::Zoom,
            kind: ControlKind::F32,
            min_f32: Some(1.0),
            max_f32: None,
            min_u32: None,
            max_u32: None,
        },
        ControlInfo {
            id: ControlId::Brightness,
            kind: ControlKind::F32,
            min_f32: Some(-1.0),
            max_f32: Some(1.0),
            min_u32: None,
            max_u32: None,
        },
        ControlInfo {
            id: ControlId::Contrast,
            kind: ControlKind::F32,
            min_f32: Some(-1.0),
            max_f32: Some(1.0),
            min_u32: None,
            max_u32: None,
        },
        ControlInfo {
            id: ControlId::Saturation,
            kind: ControlKind::F32,
            min_f32: Some(-1.0),
            max_f32: Some(1.0),
            min_u32: None,
            max_u32: None,
        },
        ControlInfo {
            id: ControlId::Sharpness,
            kind: ControlKind::F32,
            min_f32: Some(-1.0),
            max_f32: Some(1.0),
            min_u32: None,
            max_u32: None,
        },
        ControlInfo {
            id: ControlId::NoiseReduction,
            kind: ControlKind::Bool,
            min_f32: None,
            max_f32: None,
            min_u32: None,
            max_u32: None,
        },
        ControlInfo {
            id: ControlId::ImageStabilization,
            kind: ControlKind::Bool,
            min_f32: None,
            max_f32: None,
            min_u32: None,
            max_u32: None,
        },
    ]
}

pub fn validate_control_value(id: ControlId, value: &ControlValue) -> Result<(), HeadlessError> {
    let info = all_controls()
        .into_iter()
        .find(|c| c.id == id)
        .ok_or_else(|| HeadlessError::unsupported(format!("control {id:?} not supported")))?;

    match (info.kind, value) {
        (ControlKind::Bool, ControlValue::Bool(_)) => Ok(()),
        (ControlKind::F32, ControlValue::F32(v)) => {
            if let Some(min) = info.min_f32 {
                if *v < min {
                    return Err(HeadlessError::invalid_argument("value below minimum"));
                }
            }
            if let Some(max) = info.max_f32 {
                if *v > max {
                    return Err(HeadlessError::invalid_argument("value above maximum"));
                }
            }
            Ok(())
        }
        (ControlKind::U32, ControlValue::U32(v)) => {
            if let Some(min) = info.min_u32 {
                if *v < min {
                    return Err(HeadlessError::invalid_argument("value below minimum"));
                }
            }
            if let Some(max) = info.max_u32 {
                if *v > max {
                    return Err(HeadlessError::invalid_argument("value above maximum"));
                }
            }
            Ok(())
        }
        (ControlKind::WhiteBalance, ControlValue::WhiteBalance(_)) => Ok(()),
        _ => Err(HeadlessError::invalid_argument(
            "control value kind mismatch",
        )),
    }
}