1use std::time::Duration;
6
7use crate::GenCamPixelBpp;
8use serde::{Deserialize, Serialize};
9use thiserror::Error;
10
11pub type PropertyResult<T> = Result<T, PropertyError>;
13
14#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
15pub struct Property {
17 auto: bool,
18 rdonly: bool,
19 prop: PropertyLims,
20 doc: Option<String>,
21}
22
23impl Property {
24 pub fn new(prop: PropertyLims, auto_supported: bool, rdonly: bool) -> Self {
26 Property {
27 auto: auto_supported,
28 rdonly,
29 prop,
30 doc: None,
31 }
32 }
33
34 pub fn set_doc<T: Into<String>>(&mut self, doc: T) {
36 self.doc = Some(doc.into());
37 }
38
39 pub fn get_doc(&self) -> Option<&str> {
41 self.doc.as_deref()
42 }
43
44 pub fn get_type(&self) -> PropertyType {
46 (&self.prop).into()
47 }
48
49 pub fn supports_auto(&self) -> bool {
51 self.auto
52 }
53
54 pub fn validate(&self, value: &PropertyValue) -> PropertyResult<()> {
56 match self.prop {
58 PropertyLims::EnumStr { ref variants, .. } => {
59 if let PropertyValue::EnumStr(ref val) = value {
60 if variants.contains(val) {
61 return Ok(());
62 } else {
63 return Err(PropertyError::ValueNotSupported);
64 }
65 } else {
66 return Err(PropertyError::InvalidControlType {
67 expected: PropertyType::EnumStr,
68 received: value.get_type(),
69 });
70 }
71 }
72 PropertyLims::EnumInt { ref variants, .. } => {
73 if let PropertyValue::Int(ref val) = value {
74 if variants.contains(val) {
75 return Ok(());
76 } else {
77 return Err(PropertyError::ValueNotSupported);
78 }
79 } else {
80 return Err(PropertyError::InvalidControlType {
81 expected: PropertyType::EnumInt,
82 received: value.get_type(),
83 });
84 }
85 }
86 PropertyLims::EnumUnsigned { ref variants, .. } => {
87 if let PropertyValue::Unsigned(ref val) = value {
88 if variants.contains(val) {
89 return Ok(());
90 } else {
91 return Err(PropertyError::ValueNotSupported);
92 }
93 } else {
94 return Err(PropertyError::InvalidControlType {
95 expected: PropertyType::EnumUnsigned,
96 received: value.get_type(),
97 });
98 }
99 }
100 PropertyLims::Duration { .. } => {
101 if value.get_type() != PropertyType::Duration {
102 return Err(PropertyError::InvalidControlType {
103 expected: PropertyType::Duration,
104 received: value.get_type(),
105 });
106 }
107 }
108 PropertyLims::Bool { .. } => {
109 if value.get_type() != PropertyType::Bool {
110 return Err(PropertyError::InvalidControlType {
111 expected: PropertyType::Bool,
112 received: value.get_type(),
113 });
114 }
115 }
116 PropertyLims::Int { .. } => {
117 if value.get_type() != PropertyType::Int {
118 return Err(PropertyError::InvalidControlType {
119 expected: PropertyType::Int,
120 received: value.get_type(),
121 });
122 }
123 }
124 PropertyLims::Float { .. } => {
125 if value.get_type() != PropertyType::Float {
126 return Err(PropertyError::InvalidControlType {
127 expected: PropertyType::Float,
128 received: value.get_type(),
129 });
130 }
131 }
132 PropertyLims::Unsigned { .. } => {
133 if value.get_type() != PropertyType::Unsigned {
134 return Err(PropertyError::InvalidControlType {
135 expected: PropertyType::Unsigned,
136 received: value.get_type(),
137 });
138 }
139 }
140 PropertyLims::PixelFmt { .. } => {
141 if value.get_type() != PropertyType::PixelFmt {
142 return Err(PropertyError::InvalidControlType {
143 expected: PropertyType::PixelFmt,
144 received: value.get_type(),
145 });
146 }
147 }
148 }
149 match self.get_type() {
151 PropertyType::Int
152 | PropertyType::Unsigned
153 | PropertyType::Float
154 | PropertyType::Duration => {
155 if &self.get_min()? <= value && value <= &self.get_max()? {
156 Ok(())
157 } else {
158 Err(PropertyError::ValueOutOfRange {
159 value: value.clone(),
160 min: self.get_min().unwrap(), max: self.get_max().unwrap(), })
163 }
164 }
165 PropertyType::Bool => Ok(()),
166 PropertyType::Command
167 | PropertyType::PixelFmt
168 | PropertyType::EnumStr
169 | PropertyType::EnumInt
170 | PropertyType::EnumUnsigned => Err(PropertyError::NotNumber),
171 }
172 }
173
174 pub fn get_min(&self) -> PropertyResult<PropertyValue> {
176 use PropertyLims::*;
177 match &self.prop {
178 Bool { .. } => Err(PropertyError::NotNumber),
179 Int { min, .. } => Ok((*min).into()),
180 Float { min, .. } => Ok((*min).into()),
181 Unsigned { min, .. } => Ok((*min).into()),
182 Duration { min, .. } => Ok((*min).into()),
183 PixelFmt { variants, .. } => {
184 Ok((*variants.iter().min().ok_or(PropertyError::EmptyEnumList)?).into())
185 }
186 EnumStr { .. } => Err(PropertyError::NotNumber),
187 EnumInt { variants, .. } => {
188 Ok((*variants.iter().min().ok_or(PropertyError::EmptyEnumList)?).into())
189 }
190 EnumUnsigned { variants, .. } => {
191 Ok((*variants.iter().min().ok_or(PropertyError::EmptyEnumList)?).into())
192 }
193 }
194 }
195
196 pub fn get_max(&self) -> PropertyResult<PropertyValue> {
198 use PropertyLims::*;
199 match &self.prop {
200 Bool { .. } => Err(PropertyError::NotNumber),
201 Int { max, .. } => Ok((*max).into()),
202 Float { max, .. } => Ok((*max).into()),
203 Unsigned { max, .. } => Ok((*max).into()),
204 Duration { max, .. } => Ok((*max).into()),
205 PixelFmt { variants, .. } => {
206 Ok((*variants.iter().max().ok_or(PropertyError::EmptyEnumList)?).into())
207 }
208 EnumStr { .. } => Err(PropertyError::NotNumber),
209 EnumInt { variants, .. } => {
210 Ok((*variants.iter().max().ok_or(PropertyError::EmptyEnumList)?).into())
211 }
212 EnumUnsigned { variants, .. } => {
213 Ok((*variants.iter().max().ok_or(PropertyError::EmptyEnumList)?).into())
214 }
215 }
216 }
217
218 pub fn get_step(&self) -> PropertyResult<PropertyValue> {
220 use PropertyLims::*;
221 match &self.prop {
222 Bool { .. } => Err(PropertyError::NotNumber),
223 Int { step, .. } => Ok((*step).into()),
224 Float { step, .. } => Ok((*step).into()),
225 Unsigned { step, .. } => Ok((*step).into()),
226 Duration { step, .. } => Ok((*step).into()),
227 PixelFmt { .. } => Err(PropertyError::IsEnum),
228 EnumStr { .. } => Err(PropertyError::NotNumber),
229 EnumInt { .. } => Err(PropertyError::IsEnum),
230 EnumUnsigned { .. } => Err(PropertyError::IsEnum),
231 }
232 }
233
234 pub fn get_default(&self) -> PropertyResult<PropertyValue> {
236 use PropertyLims::*;
237 match self.prop.clone() {
238 Bool { default } => Ok(default.into()),
239 Int { default, .. } => Ok(default.into()),
240 Float { default, .. } => Ok(default.into()),
241 Unsigned { default, .. } => Ok(default.into()),
242 Duration { default, .. } => Ok(default.into()),
243 PixelFmt { default, .. } => Ok(default.into()),
244 EnumStr { default, .. } => Ok(default.into()),
245 EnumInt { default, .. } => Ok(default.into()),
246 EnumUnsigned { default, .. } => Ok(default.into()),
247 }
248 }
249
250 pub fn get_variants(&self) -> PropertyResult<Vec<PropertyValue>> {
252 use PropertyLims::*;
253 match &self.prop {
254 Bool { .. } | Int { .. } | Float { .. } | Unsigned { .. } | Duration { .. } => {
255 Err(PropertyError::NotEnum)
256 }
257 PixelFmt { variants, .. } => Ok(variants.iter().map(|x| (*x).into()).collect()),
258 EnumStr { variants, .. } => Ok(variants.iter().map(|x| x.clone().into()).collect()),
259 EnumInt { variants, .. } => Ok(variants.iter().map(|x| (*x).into()).collect()),
260 EnumUnsigned { variants, .. } => Ok(variants.iter().map(|x| (*x).into()).collect()),
261 }
262 }
263}
264
265#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
266#[non_exhaustive]
267pub enum PropertyLims {
269 Bool {
271 default: bool,
273 },
274 Int {
276 min: i64,
278 max: i64,
280 step: i64,
282 default: i64,
284 },
285 Float {
287 min: f64,
289 max: f64,
291 step: f64,
293 default: f64,
295 },
296 Unsigned {
298 min: u64,
300 max: u64,
302 step: u64,
304 default: u64,
306 },
307 Duration {
309 min: Duration,
311 max: Duration,
313 step: Duration,
315 default: Duration,
317 },
318 PixelFmt {
320 variants: Vec<GenCamPixelBpp>,
322 default: GenCamPixelBpp,
324 },
325 EnumStr {
327 variants: Vec<String>,
329 default: String,
331 },
332 EnumInt {
334 variants: Vec<i64>,
336 default: i64,
338 },
339 EnumUnsigned {
341 variants: Vec<u64>,
343 default: u64,
345 },
346}
347
348#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, PartialOrd)]
349#[non_exhaustive]
350pub enum PropertyValue {
352 Command,
354 Bool(bool),
356 Int(i64),
358 Float(f64),
360 Unsigned(u64),
362 PixelFmt(GenCamPixelBpp),
364 Duration(Duration),
366 EnumStr(String),
368}
369
370impl PropertyValue {
371 pub fn get_type(&self) -> PropertyType {
373 self.into()
374 }
375}
376
377impl From<()> for PropertyValue {
378 fn from(_: ()) -> Self {
379 PropertyValue::Command
380 }
381}
382
383impl From<i64> for PropertyValue {
384 fn from(val: i64) -> Self {
385 PropertyValue::Int(val)
386 }
387}
388
389impl From<u64> for PropertyValue {
390 fn from(val: u64) -> Self {
391 PropertyValue::Unsigned(val)
392 }
393}
394
395impl From<f64> for PropertyValue {
396 fn from(val: f64) -> Self {
397 PropertyValue::Float(val)
398 }
399}
400
401impl From<Duration> for PropertyValue {
402 fn from(val: Duration) -> Self {
403 PropertyValue::Duration(val)
404 }
405}
406
407impl From<String> for PropertyValue {
408 fn from(val: String) -> Self {
409 PropertyValue::EnumStr(val)
410 }
411}
412
413impl From<&str> for PropertyValue {
414 fn from(val: &str) -> Self {
415 PropertyValue::EnumStr(val.to_owned())
416 }
417}
418
419impl From<bool> for PropertyValue {
420 fn from(val: bool) -> Self {
421 PropertyValue::Bool(val)
422 }
423}
424
425impl From<GenCamPixelBpp> for PropertyValue {
426 fn from(val: GenCamPixelBpp) -> Self {
427 PropertyValue::PixelFmt(val)
428 }
429}
430
431impl TryFrom<PropertyValue> for () {
432 type Error = PropertyError;
433
434 fn try_from(value: PropertyValue) -> Result<Self, Self::Error> {
435 match value {
436 PropertyValue::Command => Ok(()),
437 _ => Err(PropertyError::InvalidControlType {
438 expected: PropertyType::Command,
439 received: value.get_type(),
440 }),
441 }
442 }
443}
444
445impl TryFrom<&PropertyValue> for () {
446 type Error = PropertyError;
447
448 fn try_from(value: &PropertyValue) -> Result<Self, Self::Error> {
449 match value {
450 PropertyValue::Command => Ok(()),
451 _ => Err(PropertyError::InvalidControlType {
452 expected: PropertyType::Command,
453 received: value.get_type(),
454 }),
455 }
456 }
457}
458
459macro_rules! tryfrom_impl_propval {
460 ($type:ty, $variant:ident) => {
461 impl TryFrom<PropertyValue> for $type {
462 type Error = PropertyError;
463
464 fn try_from(value: PropertyValue) -> Result<Self, Self::Error> {
465 match value {
466 PropertyValue::$variant(val) => Ok(val),
467 _ => Err(PropertyError::InvalidControlType {
468 expected: PropertyType::$variant,
469 received: value.get_type(),
470 }),
471 }
472 }
473 }
474 };
475}
476
477tryfrom_impl_propval!(bool, Bool);
478tryfrom_impl_propval!(i64, Int);
479tryfrom_impl_propval!(f64, Float);
480tryfrom_impl_propval!(u64, Unsigned);
481tryfrom_impl_propval!(Duration, Duration);
482tryfrom_impl_propval!(String, EnumStr);
483tryfrom_impl_propval!(GenCamPixelBpp, PixelFmt);
484
485macro_rules! tryfrom_impl_propvalref {
486 ($type:ty, $variant:ident) => {
487 impl TryFrom<&PropertyValue> for $type {
488 type Error = PropertyError;
489
490 fn try_from(value: &PropertyValue) -> Result<Self, Self::Error> {
491 match value {
492 PropertyValue::$variant(val) => Ok(val.clone()),
493 _ => Err(PropertyError::InvalidControlType {
494 expected: PropertyType::$variant,
495 received: value.get_type(),
496 }),
497 }
498 }
499 }
500 };
501}
502
503tryfrom_impl_propvalref!(bool, Bool);
504tryfrom_impl_propvalref!(i64, Int);
505tryfrom_impl_propvalref!(f64, Float);
506tryfrom_impl_propvalref!(u64, Unsigned);
507tryfrom_impl_propvalref!(Duration, Duration);
508tryfrom_impl_propvalref!(String, EnumStr);
509tryfrom_impl_propvalref!(GenCamPixelBpp, PixelFmt);
510
511impl From<&PropertyValue> for PropertyType {
512 fn from(prop: &PropertyValue) -> Self {
513 use PropertyValue::*;
514 match prop {
515 Command => PropertyType::Command,
516 Bool(_) => PropertyType::Bool,
517 Int(_) => PropertyType::Int,
518 Float(_) => PropertyType::Float,
519 Unsigned(_) => PropertyType::Unsigned,
520 PixelFmt(_) => PropertyType::PixelFmt,
521 Duration(_) => PropertyType::Duration,
522 EnumStr(_) => PropertyType::EnumStr,
523 }
524 }
525}
526
527impl PropertyValue {
528 pub fn as_bool(&self) -> Option<bool> {
530 match self {
531 PropertyValue::Bool(val) => Some(*val),
532 _ => None,
533 }
534 }
535 pub fn as_i64(&self) -> Option<i64> {
537 match self {
538 PropertyValue::Int(val) => Some(*val),
539 _ => None,
540 }
541 }
542 pub fn as_f64(&self) -> Option<f64> {
544 match self {
545 PropertyValue::Float(val) => Some(*val),
546 _ => None,
547 }
548 }
549 pub fn as_u64(&self) -> Option<u64> {
551 match self {
552 PropertyValue::Unsigned(val) => Some(*val),
553 _ => None,
554 }
555 }
556 pub fn as_duration(&self) -> Option<Duration> {
558 match self {
559 PropertyValue::Duration(val) => Some(*val),
560 _ => None,
561 }
562 }
563 pub fn as_pixel_fmt(&self) -> Option<GenCamPixelBpp> {
565 match self {
566 PropertyValue::PixelFmt(val) => Some(*val),
567 _ => None,
568 }
569 }
570 pub fn as_enum_str(&self) -> Option<&str> {
572 match self {
573 PropertyValue::EnumStr(val) => Some(val),
574 _ => None,
575 }
576 }
577}
578
579#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
580#[non_exhaustive]
581pub enum PropertyType {
583 Command,
585 Bool,
587 Int,
589 Float,
591 Unsigned,
593 PixelFmt,
595 Duration,
597 EnumStr,
599 EnumInt,
601 EnumUnsigned,
603}
604
605impl From<&PropertyLims> for PropertyType {
606 fn from(prop: &PropertyLims) -> Self {
607 use PropertyLims::*;
608 match prop {
609 Bool { .. } => PropertyType::Bool,
610 Int { .. } => PropertyType::Int,
611 Float { .. } => PropertyType::Float,
612 Unsigned { .. } => PropertyType::Unsigned,
613 Duration { .. } => PropertyType::Duration,
614 PixelFmt { .. } => PropertyType::PixelFmt,
615 EnumStr { .. } => PropertyType::EnumStr,
616 EnumInt { .. } => PropertyType::EnumInt,
617 EnumUnsigned { .. } => PropertyType::EnumUnsigned,
618 }
619 }
620}
621
622#[derive(Error, Debug, Clone, PartialEq, Serialize, Deserialize)]
623pub enum PropertyError {
625 #[error("Property not found")]
627 NotFound,
628 #[error("Property is read only")]
630 ReadOnly,
631 #[error("Property is not an enum")]
633 NotEnum,
634 #[error("Property is not a number")]
636 NotNumber,
637 #[error("Value out of range")]
638 ValueOutOfRange {
640 min: PropertyValue,
642 max: PropertyValue,
644 value: PropertyValue,
646 },
647 #[error("Value not supported")]
648 ValueNotSupported,
650 #[error("Property is an enum")]
652 IsEnum,
653 #[error("Auto mode not supported")]
655 AutoNotSupported,
656 #[error("Invalid control type: {expected:?} != {received:?}")]
657 InvalidControlType {
659 expected: PropertyType,
661 received: PropertyType,
663 },
664 #[error("Empty enum list")]
665 EmptyEnumList,
667}