1 #[cfg(feature = "serde")]
2 use serde::{Deserialize, Serialize};
3 
4 use crate::MediaTrackConstraintResolutionStrategy;
5 
6 /// A bare value or constraint specifying a range of accepted values.
7 ///
8 /// # W3C Spec Compliance
9 ///
10 /// There exists no direct corresponding type in the
11 /// W3C ["Media Capture and Streams"][media_capture_and_streams_spec] spec,
12 /// since the `ValueConstraint<T>` type aims to be a generalization over
13 /// multiple types in the spec.
14 ///
15 /// | Rust                               | W3C                                   |
16 /// | ---------------------------------- | ------------------------------------- |
17 /// | `ValueRangeConstraint<u64>` | [`ConstrainULong`][constrain_ulong]   |
18 /// | `ValueRangeConstraint<f64>` | [`ConstrainDouble`][constrain_double] |
19 ///
20 /// [constrain_double]: https://www.w3.org/TR/mediacapture-streams/#dom-constraindouble
21 /// [constrain_ulong]: https://www.w3.org/TR/mediacapture-streams/#dom-constrainulong
22 /// [media_capture_and_streams_spec]: https://www.w3.org/TR/mediacapture-streams/
23 #[derive(Debug, Clone, Eq, PartialEq)]
24 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
25 #[cfg_attr(feature = "serde", serde(untagged))]
26 pub enum ValueRangeConstraint<T> {
27     Bare(T),
28     Constraint(ResolvedValueRangeConstraint<T>),
29 }
30 
31 impl<T> Default for ValueRangeConstraint<T> {
32     fn default() -> Self {
33         Self::Constraint(Default::default())
34     }
35 }
36 
37 impl<T> From<T> for ValueRangeConstraint<T> {
38     fn from(bare: T) -> Self {
39         Self::Bare(bare)
40     }
41 }
42 
43 impl<T> From<ResolvedValueRangeConstraint<T>> for ValueRangeConstraint<T> {
44     fn from(constraint: ResolvedValueRangeConstraint<T>) -> Self {
45         Self::Constraint(constraint)
46     }
47 }
48 
49 impl<T> ValueRangeConstraint<T>
50 where
51     T: Clone,
52 {
53     pub fn to_resolved(
54         &self,
55         strategy: MediaTrackConstraintResolutionStrategy,
56     ) -> ResolvedValueRangeConstraint<T> {
57         self.clone().into_resolved(strategy)
58     }
59 
60     pub fn into_resolved(
61         self,
62         strategy: MediaTrackConstraintResolutionStrategy,
63     ) -> ResolvedValueRangeConstraint<T> {
64         match self {
65             Self::Bare(bare) => match strategy {
66                 MediaTrackConstraintResolutionStrategy::BareToIdeal => {
67                     ResolvedValueRangeConstraint::default().ideal(bare)
68                 }
69                 MediaTrackConstraintResolutionStrategy::BareToExact => {
70                     ResolvedValueRangeConstraint::default().exact(bare)
71                 }
72             },
73             Self::Constraint(constraint) => constraint,
74         }
75     }
76 }
77 
78 impl<T> ValueRangeConstraint<T> {
79     pub fn is_empty(&self) -> bool {
80         match self {
81             Self::Bare(_) => false,
82             Self::Constraint(constraint) => constraint.is_empty(),
83         }
84     }
85 }
86 
87 /// A constraint specifying a range of accepted values.
88 ///
89 /// Corresponding W3C spec types as per ["Media Capture and Streams"][spec]:
90 /// - `ConstrainDouble` => `ResolvedValueRangeConstraint<f64>`
91 /// - `ConstrainULong` => `ResolvedValueRangeConstraint<u64>`
92 ///
93 /// [spec]: https://www.w3.org/TR/mediacapture-streams
94 #[derive(Debug, Clone, Eq, PartialEq)]
95 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
96 #[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
97 pub struct ResolvedValueRangeConstraint<T> {
98     #[cfg_attr(
99         feature = "serde",
100         serde(skip_serializing_if = "core::option::Option::is_none")
101     )]
102     pub min: Option<T>,
103     #[cfg_attr(
104         feature = "serde",
105         serde(skip_serializing_if = "core::option::Option::is_none")
106     )]
107     pub max: Option<T>,
108     #[cfg_attr(
109         feature = "serde",
110         serde(skip_serializing_if = "core::option::Option::is_none")
111     )]
112     pub exact: Option<T>,
113     #[cfg_attr(
114         feature = "serde",
115         serde(skip_serializing_if = "core::option::Option::is_none")
116     )]
117     pub ideal: Option<T>,
118 }
119 
120 impl<T> ResolvedValueRangeConstraint<T> {
121     #[inline]
122     pub fn exact<U>(mut self, exact: U) -> Self
123     where
124         Option<T>: From<U>,
125     {
126         self.exact = exact.into();
127         self
128     }
129 
130     #[inline]
131     pub fn ideal<U>(mut self, ideal: U) -> Self
132     where
133         Option<T>: From<U>,
134     {
135         self.ideal = ideal.into();
136         self
137     }
138 
139     #[inline]
140     pub fn min<U>(mut self, min: U) -> Self
141     where
142         Option<T>: From<U>,
143     {
144         self.min = min.into();
145         self
146     }
147 
148     #[inline]
149     pub fn max<U>(mut self, max: U) -> Self
150     where
151         Option<T>: From<U>,
152     {
153         self.max = max.into();
154         self
155     }
156 
157     pub fn is_required(&self) -> bool {
158         self.min.is_some() || self.max.is_some() || self.exact.is_some()
159     }
160 
161     pub fn is_empty(&self) -> bool {
162         self.min.is_none() && self.max.is_none() && self.exact.is_none() && self.ideal.is_none()
163     }
164 
165     pub fn to_required_only(&self) -> Self
166     where
167         T: Clone,
168     {
169         self.clone().into_required_only()
170     }
171 
172     pub fn into_required_only(self) -> Self {
173         Self {
174             min: self.min,
175             max: self.max,
176             exact: self.exact,
177             ideal: None,
178         }
179     }
180 }
181 
182 impl<T> Default for ResolvedValueRangeConstraint<T> {
183     #[inline]
184     fn default() -> Self {
185         Self {
186             min: None,
187             max: None,
188             exact: None,
189             ideal: None,
190         }
191     }
192 }
193 
194 impl<T> std::fmt::Display for ResolvedValueRangeConstraint<T>
195 where
196     T: std::fmt::Debug,
197 {
198     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
199         let mut is_first = true;
200         f.write_str("(")?;
201         if let Some(exact) = &self.exact {
202             f.write_fmt(format_args!("x == {:?}", exact))?;
203             is_first = false;
204         } else if let (Some(min), Some(max)) = (&self.min, &self.max) {
205             f.write_fmt(format_args!("{:?} <= x <= {:?}", min, max))?;
206             is_first = false;
207         } else if let Some(min) = &self.min {
208             f.write_fmt(format_args!("{:?} <= x", min))?;
209             is_first = false;
210         } else if let Some(max) = &self.max {
211             f.write_fmt(format_args!("x <= {:?}", max))?;
212             is_first = false;
213         }
214         if let Some(ideal) = &self.ideal {
215             if !is_first {
216                 f.write_str(" && ")?;
217             }
218             f.write_fmt(format_args!("x ~= {:?}", ideal))?;
219             is_first = false;
220         }
221         if is_first {
222             f.write_str("<empty>")?;
223         }
224         f.write_str(")")?;
225         Ok(())
226     }
227 }
228 
229 #[cfg(test)]
230 mod tests {
231     use super::*;
232 
233     #[test]
234     fn to_string() {
235         let scenarios = [
236             (ResolvedValueRangeConstraint::default(), "(<empty>)"),
237             (ResolvedValueRangeConstraint::default().exact(1), "(x == 1)"),
238             (ResolvedValueRangeConstraint::default().ideal(2), "(x ~= 2)"),
239             (
240                 ResolvedValueRangeConstraint::default().exact(1).ideal(2),
241                 "(x == 1 && x ~= 2)",
242             ),
243         ];
244 
245         for (constraint, expected) in scenarios {
246             let actual = constraint.to_string();
247 
248             assert_eq!(actual, expected);
249         }
250     }
251 
252     #[test]
253     fn is_required() {
254         for min_is_some in [false, true] {
255             // TODO: Replace `if { Some(_) } else { None }` with `.then_some(_)`
256             // once MSRV has passed 1.62.0:
257             let min = if min_is_some { Some(1) } else { None };
258             for max_is_some in [false, true] {
259                 // TODO: Replace `if { Some(_) } else { None }` with `.then_some(_)`
260                 // once MSRV has passed 1.62.0:
261                 let max = if max_is_some { Some(2) } else { None };
262                 for exact_is_some in [false, true] {
263                     // TODO: Replace `if { Some(_) } else { None }` with `.then_some(_)`
264                     // once MSRV has passed 1.62.0:
265                     let exact = if exact_is_some { Some(3) } else { None };
266                     for ideal_is_some in [false, true] {
267                         // TODO: Replace `if { Some(_) } else { None }` with `.then_some(_)`
268                         // once MSRV has passed 1.62.0:
269                         let ideal = if ideal_is_some { Some(4) } else { None };
270 
271                         let constraint = ResolvedValueRangeConstraint::<u64> {
272                             min,
273                             max,
274                             exact,
275                             ideal,
276                         };
277 
278                         let actual = constraint.is_required();
279                         let expected = min_is_some || max_is_some || exact_is_some;
280 
281                         assert_eq!(actual, expected);
282                     }
283                 }
284             }
285         }
286     }
287 
288     mod is_empty {
289         use super::*;
290 
291         #[test]
292         fn bare() {
293             let constraint = ValueRangeConstraint::Bare(42);
294 
295             assert!(!constraint.is_empty());
296         }
297 
298         #[test]
299         fn constraint() {
300             for min_is_some in [false, true] {
301                 // TODO: Replace `if { Some(_) } else { None }` with `.then_some(_)`
302                 // once MSRV has passed 1.62.0:
303                 let min = if min_is_some { Some(1) } else { None };
304                 for max_is_some in [false, true] {
305                     // TODO: Replace `if { Some(_) } else { None }` with `.then_some(_)`
306                     // once MSRV has passed 1.62.0:
307                     let max = if max_is_some { Some(2) } else { None };
308                     for exact_is_some in [false, true] {
309                         // TODO: Replace `if { Some(_) } else { None }` with `.then_some(_)`
310                         // once MSRV has passed 1.62.0:
311                         let exact = if exact_is_some { Some(3) } else { None };
312                         for ideal_is_some in [false, true] {
313                             // TODO: Replace `if { Some(_) } else { None }` with `.then_some(_)`
314                             // once MSRV has passed 1.62.0:
315                             let ideal = if ideal_is_some { Some(4) } else { None };
316 
317                             let constraint = ResolvedValueRangeConstraint::<u64> {
318                                 min,
319                                 max,
320                                 exact,
321                                 ideal,
322                             };
323 
324                             let actual = constraint.is_empty();
325                             let expected =
326                                 !(min_is_some || max_is_some || exact_is_some || ideal_is_some);
327 
328                             assert_eq!(actual, expected);
329                         }
330                     }
331                 }
332             }
333         }
334     }
335 }
336 
337 #[test]
338 fn resolve_to_advanced() {
339     let constraints = [
340         ValueRangeConstraint::Bare(42),
341         ValueRangeConstraint::Constraint(ResolvedValueRangeConstraint::default().exact(42)),
342     ];
343     let strategy = MediaTrackConstraintResolutionStrategy::BareToExact;
344 
345     for constraint in constraints {
346         let actuals = [
347             constraint.to_resolved(strategy),
348             constraint.into_resolved(strategy),
349         ];
350 
351         let expected = ResolvedValueRangeConstraint::default().exact(42);
352 
353         for actual in actuals {
354             assert_eq!(actual, expected);
355         }
356     }
357 }
358 
359 #[test]
360 fn resolve_to_basic() {
361     let constraints = [
362         ValueRangeConstraint::Bare(42),
363         ValueRangeConstraint::Constraint(ResolvedValueRangeConstraint::default().ideal(42)),
364     ];
365     let strategy = MediaTrackConstraintResolutionStrategy::BareToIdeal;
366 
367     for constraint in constraints {
368         let actuals = [
369             constraint.to_resolved(strategy),
370             constraint.into_resolved(strategy),
371         ];
372 
373         let expected = ResolvedValueRangeConstraint::default().ideal(42);
374 
375         for actual in actuals {
376             assert_eq!(actual, expected);
377         }
378     }
379 }
380 
381 #[cfg(feature = "serde")]
382 #[cfg(test)]
383 mod serde_tests {
384     use crate::macros::test_serde_symmetry;
385 
386     use super::*;
387 
388     macro_rules! test_serde {
389         ($t:ty => {
390             value: $value:expr
391         }) => {
392             type Subject = ValueRangeConstraint<$t>;
393 
394             #[test]
395             fn default() {
396                 let subject = Subject::default();
397                 let json = serde_json::json!({});
398 
399                 test_serde_symmetry!(subject: subject, json: json);
400             }
401 
402             #[test]
403             fn bare() {
404                 let subject = Subject::Bare($value.to_owned());
405                 let json = serde_json::json!($value);
406 
407                 test_serde_symmetry!(subject: subject, json: json);
408             }
409 
410             #[test]
411             fn min_constraint() {
412                 let subject = Subject::Constraint(ResolvedValueRangeConstraint::default().min($value.to_owned()));
413                 let json = serde_json::json!({
414                     "min": $value,
415                 });
416 
417                 test_serde_symmetry!(subject: subject, json: json);
418             }
419 
420             #[test]
421             fn max_constraint() {
422                 let subject = Subject::Constraint(ResolvedValueRangeConstraint::default().max($value.to_owned()));
423                 let json = serde_json::json!({
424                     "max": $value,
425                 });
426 
427                 test_serde_symmetry!(subject: subject, json: json);
428             }
429 
430             #[test]
431             fn exact_constraint() {
432                 let subject = Subject::Constraint(ResolvedValueRangeConstraint::default().exact($value.to_owned()));
433                 let json = serde_json::json!({
434                     "exact": $value,
435                 });
436 
437                 test_serde_symmetry!(subject: subject, json: json);
438             }
439 
440             #[test]
441             fn ideal_constraint() {
442                 let subject = Subject::Constraint(ResolvedValueRangeConstraint::default().ideal($value.to_owned()));
443                 let json = serde_json::json!({
444                     "ideal": $value,
445                 });
446 
447                 test_serde_symmetry!(subject: subject, json: json);
448             }
449 
450             #[test]
451             fn full_constraint() {
452                 let subject = Subject::Constraint(ResolvedValueRangeConstraint::default().min($value.to_owned()).max($value.to_owned()).exact($value.to_owned()).ideal($value.to_owned()));
453                 let json = serde_json::json!({
454                     "min": $value,
455                     "max": $value,
456                     "exact": $value,
457                     "ideal": $value,
458                 });
459 
460                 test_serde_symmetry!(subject: subject, json: json);
461             }
462         };
463     }
464 
465     mod f64 {
466         use super::*;
467 
468         test_serde!(f64 => {
469             value: 42.0
470         });
471     }
472 
473     mod u64 {
474         use super::*;
475 
476         test_serde!(u64 => {
477             value: 42
478         });
479     }
480 }
481