1 #[cfg(feature = "serde")]
2 use serde::{Deserialize, Serialize};
3 
4 use crate::MediaTrackConstraintResolutionStrategy;
5 
6 /// A bare value or constraint specifying a sequence 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 /// | `ValueSequenceConstraint<String>` | [`ConstrainDOMString`][constrain_dom_string] |
18 ///
19 /// [constrain_dom_string]: https://www.w3.org/TR/mediacapture-streams/#dom-constraindomstring
20 /// [media_capture_and_streams_spec]: https://www.w3.org/TR/mediacapture-streams/
21 #[derive(Debug, Clone, Eq, PartialEq)]
22 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
23 #[cfg_attr(feature = "serde", serde(untagged))]
24 pub enum ValueSequenceConstraint<T> {
25     /// A bare-valued media track constraint.
26     Bare(Vec<T>),
27     /// A fully-qualified media track constraint.
28     Constraint(ResolvedValueSequenceConstraint<T>),
29 }
30 
31 impl<T> Default for ValueSequenceConstraint<T> {
default() -> Self32     fn default() -> Self {
33         Self::Constraint(Default::default())
34     }
35 }
36 
37 impl<T> From<T> for ValueSequenceConstraint<T> {
from(bare: T) -> Self38     fn from(bare: T) -> Self {
39         Self::Bare(vec![bare])
40     }
41 }
42 
43 impl<T> From<Vec<T>> for ValueSequenceConstraint<T> {
from(bare: Vec<T>) -> Self44     fn from(bare: Vec<T>) -> Self {
45         Self::Bare(bare)
46     }
47 }
48 
49 impl<T> From<ResolvedValueSequenceConstraint<T>> for ValueSequenceConstraint<T> {
from(constraint: ResolvedValueSequenceConstraint<T>) -> Self50     fn from(constraint: ResolvedValueSequenceConstraint<T>) -> Self {
51         Self::Constraint(constraint)
52     }
53 }
54 
55 impl<T> ValueSequenceConstraint<T>
56 where
57     T: Clone,
58 {
59     /// Returns a resolved representation of the constraint
60     /// with bare values resolved to fully-qualified constraints.
to_resolved( &self, strategy: MediaTrackConstraintResolutionStrategy, ) -> ResolvedValueSequenceConstraint<T>61     pub fn to_resolved(
62         &self,
63         strategy: MediaTrackConstraintResolutionStrategy,
64     ) -> ResolvedValueSequenceConstraint<T> {
65         self.clone().into_resolved(strategy)
66     }
67 
68     /// Consumes the constraint, returning a resolved representation of the
69     /// constraint with bare values resolved to fully-qualified constraints.
into_resolved( self, strategy: MediaTrackConstraintResolutionStrategy, ) -> ResolvedValueSequenceConstraint<T>70     pub fn into_resolved(
71         self,
72         strategy: MediaTrackConstraintResolutionStrategy,
73     ) -> ResolvedValueSequenceConstraint<T> {
74         match self {
75             Self::Bare(bare) => match strategy {
76                 MediaTrackConstraintResolutionStrategy::BareToIdeal => {
77                     ResolvedValueSequenceConstraint::default().ideal(bare)
78                 }
79                 MediaTrackConstraintResolutionStrategy::BareToExact => {
80                     ResolvedValueSequenceConstraint::default().exact(bare)
81                 }
82             },
83             Self::Constraint(constraint) => constraint,
84         }
85     }
86 }
87 
88 impl<T> ValueSequenceConstraint<T> {
89     /// Returns `true` if `self` is empty, otherwise `false`.
is_empty(&self) -> bool90     pub fn is_empty(&self) -> bool {
91         match self {
92             Self::Bare(bare) => bare.is_empty(),
93             Self::Constraint(constraint) => constraint.is_empty(),
94         }
95     }
96 }
97 
98 /// A constraint specifying a sequence of accepted values.
99 ///
100 /// # W3C Spec Compliance
101 ///
102 /// There exists no direct corresponding type in the
103 /// W3C ["Media Capture and Streams"][media_capture_and_streams_spec] spec,
104 /// since the `ValueSequenceConstraint<T>` type aims to be a
105 /// generalization over multiple types in the W3C spec:
106 ///
107 /// | Rust                              | W3C                                                               |
108 /// | --------------------------------- | ----------------------------------------------------------------- |
109 /// | `ResolvedValueSequenceConstraint<String>` | [`ConstrainDOMStringParameters`][constrain_dom_string_parameters] |
110 ///
111 /// [constrain_dom_string_parameters]: https://www.w3.org/TR/mediacapture-streams/#dom-constraindomstringparameters
112 /// [media_capture_and_streams_spec]: https://www.w3.org/TR/mediacapture-streams/
113 #[derive(Debug, Clone, Eq, PartialEq)]
114 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
115 #[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
116 pub struct ResolvedValueSequenceConstraint<T> {
117     /// The exact required value for this property.
118     ///
119     /// This is a required value.
120     #[cfg_attr(
121         feature = "serde",
122         serde(skip_serializing_if = "core::option::Option::is_none")
123     )]
124     pub exact: Option<Vec<T>>,
125     /// The ideal (target) value for this property.
126     ///
127     /// This is an optional value.
128     #[cfg_attr(
129         feature = "serde",
130         serde(skip_serializing_if = "core::option::Option::is_none")
131     )]
132     pub ideal: Option<Vec<T>>,
133 }
134 
135 impl<T> ResolvedValueSequenceConstraint<T> {
136     /// Consumes `self`, returning a corresponding constraint
137     /// with the exact required value set to `exact`.
138     #[inline]
exact<U>(mut self, exact: U) -> Self where Option<Vec<T>>: From<U>,139     pub fn exact<U>(mut self, exact: U) -> Self
140     where
141         Option<Vec<T>>: From<U>,
142     {
143         self.exact = exact.into();
144         self
145     }
146 
147     /// Consumes `self`, returning a corresponding constraint
148     /// with the ideal required value set to `ideal`.
149     #[inline]
ideal<U>(mut self, ideal: U) -> Self where Option<Vec<T>>: From<U>,150     pub fn ideal<U>(mut self, ideal: U) -> Self
151     where
152         Option<Vec<T>>: From<U>,
153     {
154         self.ideal = ideal.into();
155         self
156     }
157 
158     /// Returns `true` if `value.is_some()` is `true` for any of its required values,
159     /// otherwise `false`.
is_required(&self) -> bool160     pub fn is_required(&self) -> bool {
161         self.exact.is_some()
162     }
163 
164     /// Returns `true` if `value.is_none()` is `true` for all of its values,
165     /// otherwise `false`.
is_empty(&self) -> bool166     pub fn is_empty(&self) -> bool {
167         let exact_is_empty = self.exact.as_ref().map_or(true, Vec::is_empty);
168         let ideal_is_empty = self.ideal.as_ref().map_or(true, Vec::is_empty);
169         exact_is_empty && ideal_is_empty
170     }
171 
172     /// Returns a corresponding constraint containing only required values.
to_required_only(&self) -> Self where T: Clone,173     pub fn to_required_only(&self) -> Self
174     where
175         T: Clone,
176     {
177         self.clone().into_required_only()
178     }
179 
180     /// Consumes `self, returning a corresponding constraint
181     /// containing only required values.
into_required_only(self) -> Self182     pub fn into_required_only(self) -> Self {
183         Self {
184             exact: self.exact,
185             ideal: None,
186         }
187     }
188 }
189 
190 impl<T> Default for ResolvedValueSequenceConstraint<T> {
default() -> Self191     fn default() -> Self {
192         Self {
193             exact: None,
194             ideal: None,
195         }
196     }
197 }
198 
199 impl<T> std::fmt::Display for ResolvedValueSequenceConstraint<T>
200 where
201     T: std::fmt::Debug,
202 {
fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result203     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
204         let mut is_first = true;
205         f.write_str("(")?;
206         if let Some(ref exact) = &self.exact {
207             f.write_fmt(format_args!("x == {exact:?}"))?;
208             is_first = false;
209         }
210         if let Some(ref ideal) = &self.ideal {
211             if !is_first {
212                 f.write_str(" && ")?;
213             }
214             f.write_fmt(format_args!("x ~= {ideal:?}"))?;
215             is_first = false;
216         }
217         if is_first {
218             f.write_str("<empty>")?;
219         }
220         f.write_str(")")?;
221         Ok(())
222     }
223 }
224 
225 #[cfg(test)]
226 mod tests {
227     use super::*;
228 
229     #[test]
to_string()230     fn to_string() {
231         let scenarios = [
232             (ResolvedValueSequenceConstraint::default(), "(<empty>)"),
233             (
234                 ResolvedValueSequenceConstraint::default().exact(vec![1, 2]),
235                 "(x == [1, 2])",
236             ),
237             (
238                 ResolvedValueSequenceConstraint::default().ideal(vec![2, 3]),
239                 "(x ~= [2, 3])",
240             ),
241             (
242                 ResolvedValueSequenceConstraint::default()
243                     .exact(vec![1, 2])
244                     .ideal(vec![2, 3]),
245                 "(x == [1, 2] && x ~= [2, 3])",
246             ),
247         ];
248 
249         for (constraint, expected) in scenarios {
250             let actual = constraint.to_string();
251 
252             assert_eq!(actual, expected);
253         }
254     }
255 
256     #[test]
is_required()257     fn is_required() {
258         let scenarios = [
259             (ResolvedValueSequenceConstraint::default(), false),
260             (
261                 ResolvedValueSequenceConstraint::default().exact(vec![true]),
262                 true,
263             ),
264             (
265                 ResolvedValueSequenceConstraint::default().ideal(vec![true]),
266                 false,
267             ),
268             (
269                 ResolvedValueSequenceConstraint::default()
270                     .exact(vec![true])
271                     .ideal(vec![true]),
272                 true,
273             ),
274         ];
275 
276         for (constraint, expected) in scenarios {
277             let actual = constraint.is_required();
278 
279             assert_eq!(actual, expected);
280         }
281     }
282 
283     mod is_empty {
284         use super::*;
285 
286         #[test]
bare()287         fn bare() {
288             let constraint = ValueSequenceConstraint::Bare(vec![true]);
289 
290             assert!(!constraint.is_empty());
291         }
292 
293         #[test]
constraint()294         fn constraint() {
295             let scenarios = [
296                 (ResolvedValueSequenceConstraint::default(), true),
297                 (
298                     ResolvedValueSequenceConstraint::default().exact(vec![true]),
299                     false,
300                 ),
301                 (
302                     ResolvedValueSequenceConstraint::default().ideal(vec![true]),
303                     false,
304                 ),
305                 (
306                     ResolvedValueSequenceConstraint::default()
307                         .exact(vec![true])
308                         .ideal(vec![true]),
309                     false,
310                 ),
311             ];
312 
313             for (constraint, expected) in scenarios {
314                 let constraint = ValueSequenceConstraint::<bool>::Constraint(constraint);
315 
316                 let actual = constraint.is_empty();
317 
318                 assert_eq!(actual, expected);
319             }
320         }
321     }
322 
323     #[test]
resolve_to_advanced()324     fn resolve_to_advanced() {
325         let constraints = [
326             ValueSequenceConstraint::Bare(vec![true]),
327             ValueSequenceConstraint::Constraint(
328                 ResolvedValueSequenceConstraint::default().exact(vec![true]),
329             ),
330         ];
331         let strategy = MediaTrackConstraintResolutionStrategy::BareToExact;
332 
333         for constraint in constraints {
334             let actuals = [
335                 constraint.to_resolved(strategy),
336                 constraint.into_resolved(strategy),
337             ];
338 
339             let expected = ResolvedValueSequenceConstraint::default().exact(vec![true]);
340 
341             for actual in actuals {
342                 assert_eq!(actual, expected);
343             }
344         }
345     }
346 
347     #[test]
resolve_to_basic()348     fn resolve_to_basic() {
349         let constraints = [
350             ValueSequenceConstraint::Bare(vec![true]),
351             ValueSequenceConstraint::Constraint(
352                 ResolvedValueSequenceConstraint::default().ideal(vec![true]),
353             ),
354         ];
355         let strategy = MediaTrackConstraintResolutionStrategy::BareToIdeal;
356 
357         for constraint in constraints {
358             let actuals = [
359                 constraint.to_resolved(strategy),
360                 constraint.into_resolved(strategy),
361             ];
362 
363             let expected = ResolvedValueSequenceConstraint::default().ideal(vec![true]);
364 
365             for actual in actuals {
366                 assert_eq!(actual, expected);
367             }
368         }
369     }
370 }
371 
372 #[cfg(feature = "serde")]
373 #[cfg(test)]
374 mod serde_tests {
375     use crate::macros::test_serde_symmetry;
376 
377     use super::*;
378 
379     macro_rules! test_serde {
380         ($t:ty => {
381             values: [$($values:expr),*]
382         }) => {
383             type Subject = ValueSequenceConstraint<$t>;
384 
385             #[test]
386             fn default() {
387                 let subject = Subject::default();
388                 let json = serde_json::json!({});
389 
390                 test_serde_symmetry!(subject: subject, json: json);
391             }
392 
393             #[test]
394             fn bare() {
395                 let subject = Subject::Bare(vec![$($values.to_owned()),*].into());
396                 let json = serde_json::json!([$($values),*]);
397 
398                 test_serde_symmetry!(subject: subject, json: json);
399             }
400 
401             #[test]
402             fn exact_constraint() {
403                 let subject = Subject::Constraint(ResolvedValueSequenceConstraint::default().exact(vec![$($values.to_owned()),*]));
404                 let json = serde_json::json!({
405                     "exact": [$($values),*],
406                 });
407 
408                 test_serde_symmetry!(subject: subject, json: json);
409             }
410 
411             #[test]
412             fn ideal_constraint() {
413                 let subject = Subject::Constraint(ResolvedValueSequenceConstraint::default().ideal(vec![$($values.to_owned()),*]));
414                 let json = serde_json::json!({
415                     "ideal": [$($values),*],
416                 });
417 
418                 test_serde_symmetry!(subject: subject, json: json);
419             }
420 
421             #[test]
422             fn full_constraint() {
423                 let subject = Subject::Constraint(ResolvedValueSequenceConstraint::default().exact(vec![$($values.to_owned()),*]).ideal(vec![$($values.to_owned()),*]));
424                 let json = serde_json::json!({
425                     "exact": [$($values),*],
426                     "ideal": [$($values),*],
427                 });
428 
429                 test_serde_symmetry!(subject: subject, json: json);
430             }
431         };
432     }
433 
434     mod string {
435         use super::*;
436 
437         test_serde!(String => {
438             values: ["VALUE_0", "VALUE_1"]
439         });
440     }
441 }
442