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