webrtc_constraints/constraint/
value_sequence.rs

1#[cfg(feature = "serde")]
2use serde::{Deserialize, Serialize};
3
4use 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))]
24pub 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
31impl<T> Default for ValueSequenceConstraint<T> {
32    fn default() -> Self {
33        Self::Constraint(Default::default())
34    }
35}
36
37impl<T> From<T> for ValueSequenceConstraint<T> {
38    fn from(bare: T) -> Self {
39        Self::Bare(vec![bare])
40    }
41}
42
43impl<T> From<Vec<T>> for ValueSequenceConstraint<T> {
44    fn from(bare: Vec<T>) -> Self {
45        Self::Bare(bare)
46    }
47}
48
49impl<T> From<ResolvedValueSequenceConstraint<T>> for ValueSequenceConstraint<T> {
50    fn from(constraint: ResolvedValueSequenceConstraint<T>) -> Self {
51        Self::Constraint(constraint)
52    }
53}
54
55impl<T> ValueSequenceConstraint<T>
56where
57    T: Clone,
58{
59    /// Returns a resolved representation of the constraint
60    /// with bare values resolved to fully-qualified constraints.
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.
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
88impl<T> ValueSequenceConstraint<T> {
89    /// Returns `true` if `self` is empty, otherwise `false`.
90    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"))]
116pub 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
135impl<T> ResolvedValueSequenceConstraint<T> {
136    /// Consumes `self`, returning a corresponding constraint
137    /// with the exact required value set to `exact`.
138    #[inline]
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]
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`.
160    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`.
166    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.
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.
182    pub fn into_required_only(self) -> Self {
183        Self {
184            exact: self.exact,
185            ideal: None,
186        }
187    }
188}
189
190impl<T> Default for ResolvedValueSequenceConstraint<T> {
191    fn default() -> Self {
192        Self {
193            exact: None,
194            ideal: None,
195        }
196    }
197}
198
199impl<T> std::fmt::Display for ResolvedValueSequenceConstraint<T>
200where
201    T: std::fmt::Debug,
202{
203    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)]
226mod tests {
227    use super::*;
228
229    #[test]
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]
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]
287        fn bare() {
288            let constraint = ValueSequenceConstraint::Bare(vec![true]);
289
290            assert!(!constraint.is_empty());
291        }
292
293        #[test]
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]
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]
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)]
374mod 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}