1use thiserror::Error;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum CameraControl {
9 Zoom,
10 Focus,
11 Exposure,
12 PowerLineFrequency,
13 LowLightCompensation,
14 Brightness,
15 Contrast,
16 Saturation,
17 Sharpness,
18 WhiteBalance,
19 Tint,
20}
21
22impl CameraControl {
23 pub const ALL: [Self; 11] = [
25 Self::Zoom,
26 Self::Focus,
27 Self::Exposure,
28 Self::PowerLineFrequency,
29 Self::LowLightCompensation,
30 Self::Brightness,
31 Self::Contrast,
32 Self::Saturation,
33 Self::Sharpness,
34 Self::WhiteBalance,
35 Self::Tint,
36 ];
37
38 #[must_use]
40 pub fn name(self) -> &'static str {
41 match self {
42 Self::Zoom => "zoom",
43 Self::Focus => "focus",
44 Self::Exposure => "exposure",
45 Self::PowerLineFrequency => "power_line_frequency",
46 Self::LowLightCompensation => "low_light_compensation",
47 Self::Brightness => "brightness",
48 Self::Contrast => "contrast",
49 Self::Saturation => "saturation",
50 Self::Sharpness => "sharpness",
51 Self::WhiteBalance => "white_balance",
52 Self::Tint => "tint",
53 }
54 }
55
56 #[must_use]
58 pub fn auto_toggle(self) -> Option<AutoToggle> {
59 match self {
60 Self::Focus => Some(AutoToggle::Focus),
61 Self::Exposure => Some(AutoToggle::Exposure),
62 Self::WhiteBalance => Some(AutoToggle::WhiteBalance),
63 _ => None,
64 }
65 }
66}
67
68#[derive(Debug, Clone, Copy, PartialEq, Eq)]
71pub enum AutoToggle {
72 Focus,
73 Exposure,
74 WhiteBalance,
75}
76
77impl AutoToggle {
78 pub const ALL: [Self; 3] = [Self::Focus, Self::Exposure, Self::WhiteBalance];
80
81 #[must_use]
83 pub fn name(self) -> &'static str {
84 match self {
85 Self::Focus => "focus_auto",
86 Self::Exposure => "exposure_auto",
87 Self::WhiteBalance => "white_balance_auto",
88 }
89 }
90}
91
92#[derive(Debug, Clone, Copy, PartialEq, Eq)]
94pub struct AutoState {
95 pub current: bool,
96 pub default: bool,
97}
98
99#[derive(Debug, Clone, Default)]
102pub struct CameraState {
103 pub controls: Vec<(CameraControl, ControlRange)>,
104 pub autos: Vec<(AutoToggle, AutoState)>,
105}
106
107#[derive(Debug, Clone, Copy, PartialEq, Eq)]
109pub struct ControlRange {
110 pub min: i32,
111 pub max: i32,
112 pub default: i32,
113 pub current: i32,
114 pub value_mask: Option<u32>,
117}
118
119impl ControlRange {
120 #[must_use]
122 pub fn supports(self, value: i32) -> bool {
123 if !(self.min..=self.max).contains(&value) {
124 return false;
125 }
126 self.value_mask.is_none_or(|mask| {
127 u32::try_from(value)
128 .ok()
129 .filter(|value| *value < u32::BITS)
130 .is_some_and(|value| mask & (1 << value) != 0)
131 })
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use super::ControlRange;
138
139 #[test]
140 fn discrete_range_rejects_missing_values() {
141 let range = ControlRange {
142 min: 0,
143 max: 3,
144 default: 1,
145 current: 1,
146 value_mask: Some((1 << 1) | (1 << 3)),
147 };
148
149 assert!(range.supports(1));
150 assert!(!range.supports(2));
151 assert!(range.supports(3));
152 }
153}
154
155#[derive(Debug, Clone, Error)]
157pub enum ControlError {
158 #[error("no matching UVC device")]
160 NotFound,
161 #[error("camera could not be uniquely identified")]
165 Ambiguous,
166 #[error("camera does not support that control")]
169 Unsupported,
170 #[error("platform error: {0}")]
172 Io(String),
173}