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