xref: /webrtc/constraints/src/constraint.rs (revision 6ac0fffd)
1 use std::ops::Deref;
2 
3 #[cfg(feature = "serde")]
4 use serde::{Deserialize, Serialize};
5 
6 pub use self::{
7     value::{BareOrValueConstraint, ValueConstraint},
8     value_range::{BareOrValueRangeConstraint, ValueRangeConstraint},
9     value_sequence::{BareOrValueSequenceConstraint, ValueSequenceConstraint},
10 };
11 
12 mod value;
13 mod value_range;
14 mod value_sequence;
15 
16 /// An empty [constraint][media_track_constraints] value for a [`MediaStreamTrack`][media_stream_track] object.
17 ///
18 /// # W3C Spec Compliance
19 ///
20 /// There exists no corresponding type in the W3C ["Media Capture and Streams"][media_capture_and_streams_spec] spec.
21 ///
22 /// The purpose of this type is to reduce parsing ambiguity, since all constraint variant types
23 /// support serializing from an empty map, but an empty map isn't typed, really,
24 /// so parsing to a specifically typed constraint would be wrong, type-wise.
25 ///
26 /// [media_stream_track]: https://www.w3.org/TR/mediacapture-streams/#dom-mediastreamtrack
27 /// [media_track_constraints]: https://www.w3.org/TR/mediacapture-streams/#dom-mediatrackconstraints
28 /// [media_capture_and_streams_spec]: https://www.w3.org/TR/mediacapture-streams
29 #[derive(Debug, Clone, PartialEq)]
30 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
31 #[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
32 pub struct EmptyConstraint {}
33 
34 /// The strategy of a track [constraint][constraint].
35 ///
36 /// [constraint]: https://www.w3.org/TR/mediacapture-streams/#dfn-constraint
37 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
38 pub enum MediaTrackConstraintResolutionStrategy {
39     /// Resolve bare values to `ideal` constraints.
40     BareToIdeal,
41     /// Resolve bare values to `exact` constraints.
42     BareToExact,
43 }
44 
45 /// A single [constraint][media_track_constraints] value for a [`MediaStreamTrack`][media_stream_track] object.
46 ///
47 /// # W3C Spec Compliance
48 ///
49 /// There exists no corresponding type in the W3C ["Media Capture and Streams"][media_capture_and_streams_spec] spec.
50 ///
51 /// [media_stream_track]: https://www.w3.org/TR/mediacapture-streams/#dom-mediastreamtrack
52 /// [media_track_constraints]: https://www.w3.org/TR/mediacapture-streams/#dom-mediatrackconstraints
53 /// [media_capture_and_streams_spec]: https://www.w3.org/TR/mediacapture-streams
54 #[derive(Debug, Clone, PartialEq)]
55 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
56 #[cfg_attr(feature = "serde", serde(untagged))]
57 pub enum BareOrMediaTrackConstraint {
58     Empty(EmptyConstraint),
59     // `IntegerRange` must be ordered before `FloatRange(…)` in order for
60     // `serde` to decode the correct variant.
61     IntegerRange(BareOrValueRangeConstraint<u64>),
62     FloatRange(BareOrValueRangeConstraint<f64>),
63     // `Bool` must be ordered after `IntegerRange(…)`/`FloatRange(…)` in order for
64     // `serde` to decode the correct variant.
65     Bool(BareOrValueConstraint<bool>),
66     // `StringSequence` must be ordered before `String(…)` in order for
67     // `serde` to decode the correct variant.
68     StringSequence(BareOrValueSequenceConstraint<String>),
69     String(BareOrValueConstraint<String>),
70 }
71 
72 impl Default for BareOrMediaTrackConstraint {
73     fn default() -> Self {
74         Self::Empty(EmptyConstraint {})
75     }
76 }
77 
78 // Bool constraint:
79 
80 impl From<bool> for BareOrMediaTrackConstraint {
81     fn from(bare: bool) -> Self {
82         Self::Bool(bare.into())
83     }
84 }
85 
86 impl From<ValueConstraint<bool>> for BareOrMediaTrackConstraint {
87     fn from(constraint: ValueConstraint<bool>) -> Self {
88         Self::Bool(constraint.into())
89     }
90 }
91 
92 impl From<BareOrValueConstraint<bool>> for BareOrMediaTrackConstraint {
93     fn from(constraint: BareOrValueConstraint<bool>) -> Self {
94         Self::Bool(constraint)
95     }
96 }
97 
98 // Unsigned integer range constraint:
99 
100 impl From<u64> for BareOrMediaTrackConstraint {
101     fn from(bare: u64) -> Self {
102         Self::IntegerRange(bare.into())
103     }
104 }
105 
106 impl From<ValueRangeConstraint<u64>> for BareOrMediaTrackConstraint {
107     fn from(constraint: ValueRangeConstraint<u64>) -> Self {
108         Self::IntegerRange(constraint.into())
109     }
110 }
111 
112 impl From<BareOrValueRangeConstraint<u64>> for BareOrMediaTrackConstraint {
113     fn from(constraint: BareOrValueRangeConstraint<u64>) -> Self {
114         Self::IntegerRange(constraint)
115     }
116 }
117 
118 // Floating-point range constraint:
119 
120 impl From<f64> for BareOrMediaTrackConstraint {
121     fn from(bare: f64) -> Self {
122         Self::FloatRange(bare.into())
123     }
124 }
125 
126 impl From<ValueRangeConstraint<f64>> for BareOrMediaTrackConstraint {
127     fn from(constraint: ValueRangeConstraint<f64>) -> Self {
128         Self::FloatRange(constraint.into())
129     }
130 }
131 
132 impl From<BareOrValueRangeConstraint<f64>> for BareOrMediaTrackConstraint {
133     fn from(constraint: BareOrValueRangeConstraint<f64>) -> Self {
134         Self::FloatRange(constraint)
135     }
136 }
137 
138 // String sequence constraint:
139 
140 impl From<Vec<String>> for BareOrMediaTrackConstraint {
141     fn from(bare: Vec<String>) -> Self {
142         Self::StringSequence(bare.into())
143     }
144 }
145 
146 impl From<Vec<&str>> for BareOrMediaTrackConstraint {
147     fn from(bare: Vec<&str>) -> Self {
148         let bare: Vec<String> = bare.into_iter().map(|c| c.to_owned()).collect();
149         Self::from(bare)
150     }
151 }
152 
153 impl From<ValueSequenceConstraint<String>> for BareOrMediaTrackConstraint {
154     fn from(constraint: ValueSequenceConstraint<String>) -> Self {
155         Self::StringSequence(constraint.into())
156     }
157 }
158 
159 impl From<BareOrValueSequenceConstraint<String>> for BareOrMediaTrackConstraint {
160     fn from(constraint: BareOrValueSequenceConstraint<String>) -> Self {
161         Self::StringSequence(constraint)
162     }
163 }
164 
165 // String constraint:
166 
167 impl From<String> for BareOrMediaTrackConstraint {
168     fn from(bare: String) -> Self {
169         Self::String(bare.into())
170     }
171 }
172 
173 impl<'a> From<&'a str> for BareOrMediaTrackConstraint {
174     fn from(bare: &'a str) -> Self {
175         let bare: String = bare.to_owned();
176         Self::from(bare)
177     }
178 }
179 
180 impl From<ValueConstraint<String>> for BareOrMediaTrackConstraint {
181     fn from(constraint: ValueConstraint<String>) -> Self {
182         Self::String(constraint.into())
183     }
184 }
185 
186 impl From<BareOrValueConstraint<String>> for BareOrMediaTrackConstraint {
187     fn from(constraint: BareOrValueConstraint<String>) -> Self {
188         Self::String(constraint)
189     }
190 }
191 
192 impl BareOrMediaTrackConstraint {
193     pub fn is_empty(&self) -> bool {
194         match self {
195             Self::Empty(_) => true,
196             Self::IntegerRange(constraint) => constraint.is_empty(),
197             Self::FloatRange(constraint) => constraint.is_empty(),
198             Self::Bool(constraint) => constraint.is_empty(),
199             Self::StringSequence(constraint) => constraint.is_empty(),
200             Self::String(constraint) => constraint.is_empty(),
201         }
202     }
203 
204     pub fn to_resolved(
205         &self,
206         strategy: MediaTrackConstraintResolutionStrategy,
207     ) -> MediaTrackConstraint {
208         self.clone().into_resolved(strategy)
209     }
210 
211     pub fn into_resolved(
212         self,
213         strategy: MediaTrackConstraintResolutionStrategy,
214     ) -> MediaTrackConstraint {
215         match self {
216             Self::Empty(constraint) => MediaTrackConstraint::Empty(constraint),
217             Self::IntegerRange(constraint) => {
218                 MediaTrackConstraint::IntegerRange(constraint.into_resolved(strategy))
219             }
220             Self::FloatRange(constraint) => {
221                 MediaTrackConstraint::FloatRange(constraint.into_resolved(strategy))
222             }
223             Self::Bool(constraint) => {
224                 MediaTrackConstraint::Bool(constraint.into_resolved(strategy))
225             }
226             Self::StringSequence(constraint) => {
227                 MediaTrackConstraint::StringSequence(constraint.into_resolved(strategy))
228             }
229             Self::String(constraint) => {
230                 MediaTrackConstraint::String(constraint.into_resolved(strategy))
231             }
232         }
233     }
234 }
235 
236 /// A single [constraint][media_track_constraints] value for a [`MediaStreamTrack`][media_stream_track] object
237 /// with its potential bare value either resolved to an `exact` or `ideal` constraint.
238 ///
239 /// # W3C Spec Compliance
240 ///
241 /// There exists no corresponding type in the W3C ["Media Capture and Streams"][media_capture_and_streams_spec] spec.
242 ///
243 /// [media_stream_track]: https://www.w3.org/TR/mediacapture-streams/#dom-mediastreamtrack
244 /// [media_track_constraints]: https://www.w3.org/TR/mediacapture-streams/#dom-mediatrackconstraints
245 /// [media_capture_and_streams_spec]: https://www.w3.org/TR/mediacapture-streams
246 #[derive(Debug, Clone, PartialEq)]
247 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
248 #[cfg_attr(feature = "serde", serde(untagged))]
249 pub enum MediaTrackConstraint {
250     Empty(EmptyConstraint),
251     IntegerRange(ValueRangeConstraint<u64>),
252     FloatRange(ValueRangeConstraint<f64>),
253     Bool(ValueConstraint<bool>),
254     StringSequence(ValueSequenceConstraint<String>),
255     String(ValueConstraint<String>),
256 }
257 
258 impl Default for MediaTrackConstraint {
259     fn default() -> Self {
260         Self::Empty(EmptyConstraint {})
261     }
262 }
263 
264 impl MediaTrackConstraint {
265     pub fn is_required(&self) -> bool {
266         match self {
267             Self::Empty(_constraint) => false,
268             Self::IntegerRange(constraint) => constraint.is_required(),
269             Self::FloatRange(constraint) => constraint.is_required(),
270             Self::Bool(constraint) => constraint.is_required(),
271             Self::StringSequence(constraint) => constraint.is_required(),
272             Self::String(constraint) => constraint.is_required(),
273         }
274     }
275 
276     pub fn is_empty(&self) -> bool {
277         match self {
278             Self::Empty(_constraint) => true,
279             Self::IntegerRange(constraint) => constraint.is_empty(),
280             Self::FloatRange(constraint) => constraint.is_empty(),
281             Self::Bool(constraint) => constraint.is_empty(),
282             Self::StringSequence(constraint) => constraint.is_empty(),
283             Self::String(constraint) => constraint.is_empty(),
284         }
285     }
286 
287     pub fn to_sanitized(&self) -> Option<SanitizedMediaTrackConstraint> {
288         self.clone().into_sanitized()
289     }
290 
291     pub fn into_sanitized(self) -> Option<SanitizedMediaTrackConstraint> {
292         if self.is_empty() {
293             return None;
294         }
295 
296         Some(SanitizedMediaTrackConstraint(self))
297     }
298 }
299 
300 /// A single non-empty [constraint][media_track_constraints] value for a [`MediaStreamTrack`][media_stream_track] object.
301 ///
302 /// # Invariant
303 ///
304 /// The wrapped `MediaTrackConstraint` MUST not be empty.
305 ///
306 /// To enforce this invariant the only way to create an instance of this type
307 /// is by calling `constraint.to_sanitized()`/`constraint.into_sanitized()` on
308 /// an instance of `MediaTrackConstraint`, which returns `None` if `self` is empty.
309 ///
310 /// Further more `self.0` MUST NOT be exposed mutably,
311 /// as otherwise it could become empty via mutation.
312 #[derive(Debug, Clone, PartialEq)]
313 pub struct SanitizedMediaTrackConstraint(MediaTrackConstraint);
314 
315 impl Deref for SanitizedMediaTrackConstraint {
316     type Target = MediaTrackConstraint;
317 
318     fn deref(&self) -> &Self::Target {
319         &self.0
320     }
321 }
322 
323 impl SanitizedMediaTrackConstraint {
324     pub fn into_inner(self) -> MediaTrackConstraint {
325         self.0
326     }
327 
328     pub fn integer_range(&self) -> Option<&ValueRangeConstraint<u64>> {
329         if let MediaTrackConstraint::IntegerRange(constraint) = &self.0 {
330             Some(constraint)
331         } else {
332             None
333         }
334     }
335 
336     pub fn float_range(&self) -> Option<&ValueRangeConstraint<f64>> {
337         if let MediaTrackConstraint::FloatRange(constraint) = &self.0 {
338             Some(constraint)
339         } else {
340             None
341         }
342     }
343 
344     pub fn bool(&self) -> Option<&ValueConstraint<bool>> {
345         if let MediaTrackConstraint::Bool(constraint) = &self.0 {
346             Some(constraint)
347         } else {
348             None
349         }
350     }
351 
352     pub fn string_sequence(&self) -> Option<&ValueSequenceConstraint<String>> {
353         if let MediaTrackConstraint::StringSequence(constraint) = &self.0 {
354             Some(constraint)
355         } else {
356             None
357         }
358     }
359 
360     pub fn string(&self) -> Option<&ValueConstraint<String>> {
361         if let MediaTrackConstraint::String(constraint) = &self.0 {
362             Some(constraint)
363         } else {
364             None
365         }
366     }
367 }
368 
369 #[cfg(feature = "serde")]
370 #[cfg(test)]
371 mod serde_tests {
372     use crate::macros::test_serde_symmetry;
373 
374     use super::*;
375 
376     type Subject = BareOrMediaTrackConstraint;
377 
378     #[test]
379     fn empty() {
380         let subject = Subject::Empty(EmptyConstraint {});
381         let json = serde_json::json!({});
382 
383         test_serde_symmetry!(subject: subject, json: json);
384     }
385 
386     #[test]
387     fn bool_bare() {
388         let subject = Subject::Bool(true.into());
389         let json = serde_json::json!(true);
390 
391         test_serde_symmetry!(subject: subject, json: json);
392     }
393 
394     #[test]
395     fn bool_constraint() {
396         let subject = Subject::Bool(ValueConstraint::exact_only(true).into());
397         let json = serde_json::json!({ "exact": true });
398 
399         test_serde_symmetry!(subject: subject, json: json);
400     }
401 
402     #[test]
403     fn integer_range_bare() {
404         let subject = Subject::IntegerRange(42.into());
405         let json = serde_json::json!(42);
406 
407         test_serde_symmetry!(subject: subject, json: json);
408     }
409 
410     #[test]
411     fn integer_range_constraint() {
412         let subject = Subject::IntegerRange(ValueRangeConstraint::exact_only(42).into());
413         let json = serde_json::json!({ "exact": 42 });
414 
415         test_serde_symmetry!(subject: subject, json: json);
416     }
417 
418     #[test]
419     fn float_range_bare() {
420         let subject = Subject::FloatRange(4.2.into());
421         let json = serde_json::json!(4.2);
422 
423         test_serde_symmetry!(subject: subject, json: json);
424     }
425 
426     #[test]
427     fn float_range_constraint() {
428         let subject = Subject::FloatRange(ValueRangeConstraint::exact_only(42.0).into());
429         let json = serde_json::json!({ "exact": 42.0 });
430 
431         test_serde_symmetry!(subject: subject, json: json);
432     }
433 
434     #[test]
435     fn string_sequence_bare() {
436         let subject = Subject::StringSequence(vec!["foo".to_owned(), "bar".to_owned()].into());
437         let json = serde_json::json!(["foo", "bar"]);
438 
439         test_serde_symmetry!(subject: subject, json: json);
440     }
441 
442     #[test]
443     fn string_sequence_constraint() {
444         let subject = Subject::StringSequence(
445             ValueSequenceConstraint::exact_only(vec!["foo".to_owned(), "bar".to_owned()].into())
446                 .into(),
447         );
448         let json = serde_json::json!({ "exact": ["foo", "bar"] });
449 
450         test_serde_symmetry!(subject: subject, json: json);
451     }
452 
453     #[test]
454     fn string_bare() {
455         let subject = Subject::String("foo".to_owned().into());
456         let json = serde_json::json!("foo");
457 
458         test_serde_symmetry!(subject: subject, json: json);
459     }
460 
461     #[test]
462     fn string_constraint() {
463         let subject = Subject::String(ValueConstraint::exact_only("foo".to_owned()).into());
464         let json = serde_json::json!({ "exact": "foo" });
465 
466         test_serde_symmetry!(subject: subject, json: json);
467     }
468 }
469