xref: /webrtc/constraints/src/constraint/value.rs (revision a4acb6a4)
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 `BareOrValueConstraint<T>` type aims to be a generalization over
13 /// multiple types in the spec.
14 ///
15 /// | Rust                           | W3C                                     |
16 /// | ------------------------------ | --------------------------------------- |
17 /// | `BareOrValueConstraint<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, PartialEq)]
22 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
23 #[cfg_attr(feature = "serde", serde(untagged))]
24 pub enum BareOrValueConstraint<T> {
25     Bare(T),
26     Constraint(ValueConstraint<T>),
27 }
28 
29 impl<T> Default for BareOrValueConstraint<T> {
30     fn default() -> Self {
31         Self::Constraint(Default::default())
32     }
33 }
34 
35 impl<T> From<T> for BareOrValueConstraint<T> {
36     fn from(bare: T) -> Self {
37         Self::Bare(bare)
38     }
39 }
40 
41 impl<T> From<ValueConstraint<T>> for BareOrValueConstraint<T> {
42     fn from(constraint: ValueConstraint<T>) -> Self {
43         Self::Constraint(constraint)
44     }
45 }
46 
47 impl<T> BareOrValueConstraint<T>
48 where
49     T: Clone,
50 {
51     pub fn to_resolved(
52         &self,
53         strategy: MediaTrackConstraintResolutionStrategy,
54     ) -> ValueConstraint<T> {
55         self.clone().into_resolved(strategy)
56     }
57 
58     pub fn into_resolved(
59         self,
60         strategy: MediaTrackConstraintResolutionStrategy,
61     ) -> ValueConstraint<T> {
62         match self {
63             Self::Bare(bare) => match strategy {
64                 MediaTrackConstraintResolutionStrategy::BareToIdeal => {
65                     ValueConstraint::ideal_only(bare)
66                 }
67                 MediaTrackConstraintResolutionStrategy::BareToExact => {
68                     ValueConstraint::exact_only(bare)
69                 }
70             },
71             Self::Constraint(constraint) => constraint,
72         }
73     }
74 }
75 
76 impl<T> BareOrValueConstraint<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 `BareOrValueConstraint<T>` type aims to be a
92 /// generalization over multiple types in the W3C spec:
93 ///
94 /// | Rust                           | W3C                                     |
95 /// | ------------------------------ | --------------------------------------- |
96 /// | `ValueConstraint<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, PartialEq)]
101 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
102 #[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
103 pub struct ValueConstraint<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> ValueConstraint<T> {
117     pub fn exact_only(exact: T) -> Self {
118         Self {
119             exact: Some(exact),
120             ideal: None,
121         }
122     }
123 
124     pub fn ideal_only(ideal: T) -> Self {
125         Self {
126             exact: None,
127             ideal: Some(ideal),
128         }
129     }
130 
131     pub fn is_required(&self) -> bool {
132         self.exact.is_some()
133     }
134 
135     pub fn is_empty(&self) -> bool {
136         self.exact.is_none() && self.ideal.is_none()
137     }
138 }
139 
140 impl<T> Default for ValueConstraint<T> {
141     fn default() -> Self {
142         Self {
143             exact: None,
144             ideal: None,
145         }
146     }
147 }
148 
149 #[cfg(test)]
150 mod tests {
151     use super::*;
152 
153     #[test]
154     fn resolve_to_advanced() {
155         let constraint = BareOrValueConstraint::Bare(true);
156         let strategy = MediaTrackConstraintResolutionStrategy::BareToExact;
157         let actual: ValueConstraint<bool> = constraint.into_resolved(strategy);
158         let expected = ValueConstraint::exact_only(true);
159 
160         assert_eq!(actual, expected);
161     }
162 
163     #[test]
164     fn resolve_to_basic() {
165         let constraint = BareOrValueConstraint::Bare(true);
166         let strategy = MediaTrackConstraintResolutionStrategy::BareToIdeal;
167         let actual: ValueConstraint<bool> = constraint.into_resolved(strategy);
168         let expected = ValueConstraint::ideal_only(true);
169 
170         assert_eq!(actual, expected);
171     }
172 }
173 
174 #[cfg(feature = "serde")]
175 #[cfg(test)]
176 mod serde_tests {
177     use crate::macros::test_serde_symmetry;
178 
179     use super::*;
180 
181     macro_rules! test_serde {
182         ($t:ty => {
183             value: $value:expr
184         }) => {
185             type Subject = BareOrValueConstraint<$t>;
186 
187             #[test]
188             fn default() {
189                 let subject = Subject::default();
190                 let json = serde_json::json!({});
191 
192                 test_serde_symmetry!(subject: subject, json: json);
193             }
194 
195             #[test]
196             fn bare() {
197                 let subject = Subject::Bare($value.to_owned());
198                 let json = serde_json::json!($value);
199 
200                 test_serde_symmetry!(subject: subject, json: json);
201             }
202 
203             #[test]
204             fn exact() {
205                 let subject = Subject::Constraint(ValueConstraint::exact_only($value.to_owned()));
206                 let json = serde_json::json!({
207                     "exact": $value,
208                 });
209 
210                 test_serde_symmetry!(subject: subject, json: json);
211             }
212 
213             #[test]
214             fn ideal() {
215                 let subject = Subject::Constraint(ValueConstraint::ideal_only($value.to_owned()));
216                 let json = serde_json::json!({
217                     "ideal": $value,
218                 });
219 
220                 test_serde_symmetry!(subject: subject, json: json);
221             }
222         };
223     }
224 
225     mod bool {
226         use super::*;
227 
228         test_serde!(bool => {
229             value: true
230         });
231     }
232 
233     mod string {
234         use super::*;
235 
236         test_serde!(String => {
237             value: "VALUE"
238         });
239     }
240 }
241