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