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