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