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, 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::ideal_only(bare)
68                 }
69                 MediaTrackConstraintResolutionStrategy::BareToExact => {
70                     ValueRangeConstraint::exact_only(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, 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     pub fn exact_only(exact: T) -> Self {
122         Self {
123             min: None,
124             max: None,
125             exact: Some(exact),
126             ideal: None,
127         }
128     }
129 
130     pub fn ideal_only(ideal: T) -> Self {
131         Self {
132             min: None,
133             max: None,
134             exact: None,
135             ideal: Some(ideal),
136         }
137     }
138 
139     pub fn is_required(&self) -> bool {
140         self.min.is_some() || self.max.is_some() || self.exact.is_some()
141     }
142 
143     pub fn is_empty(&self) -> bool {
144         self.min.is_none() && self.max.is_none() && self.exact.is_none() && self.ideal.is_none()
145     }
146 }
147 
148 impl<T> Default for ValueRangeConstraint<T> {
149     fn default() -> Self {
150         Self {
151             min: None,
152             max: None,
153             exact: None,
154             ideal: None,
155         }
156     }
157 }
158 
159 #[cfg(test)]
160 mod tests {
161     use super::*;
162 
163     #[test]
164     fn resolve_to_advanced() {
165         let constraint = BareOrValueRangeConstraint::Bare(42);
166         let strategy = MediaTrackConstraintResolutionStrategy::BareToExact;
167         let actual: ValueRangeConstraint<u64> = constraint.into_resolved(strategy);
168         let expected = ValueRangeConstraint::exact_only(42);
169 
170         assert_eq!(actual, expected);
171     }
172 
173     #[test]
174     fn resolve_to_basic() {
175         let constraint = BareOrValueRangeConstraint::Bare(42);
176         let strategy = MediaTrackConstraintResolutionStrategy::BareToIdeal;
177         let actual: ValueRangeConstraint<u64> = constraint.into_resolved(strategy);
178         let expected = ValueRangeConstraint::ideal_only(42);
179 
180         assert_eq!(actual, expected);
181     }
182 }
183 
184 #[cfg(feature = "serde")]
185 #[cfg(test)]
186 mod serde_tests {
187     use crate::macros::test_serde_symmetry;
188 
189     use super::*;
190 
191     macro_rules! test_serde {
192         ($t:ty => {
193             value: $value:expr
194         }) => {
195             type Subject = BareOrValueRangeConstraint<$t>;
196 
197             #[test]
198             fn default() {
199                 let subject = Subject::default();
200                 let json = serde_json::json!({});
201 
202                 test_serde_symmetry!(subject: subject, json: json);
203             }
204 
205             #[test]
206             fn bare() {
207                 let subject = Subject::Bare($value.to_owned());
208                 let json = serde_json::json!($value);
209 
210                 test_serde_symmetry!(subject: subject, json: json);
211             }
212 
213             #[test]
214             fn exact() {
215                 let subject = Subject::Constraint(ValueRangeConstraint::exact_only($value.to_owned()));
216                 let json = serde_json::json!({
217                     "exact": $value,
218                 });
219 
220                 test_serde_symmetry!(subject: subject, json: json);
221             }
222 
223             #[test]
224             fn ideal() {
225                 let subject = Subject::Constraint(ValueRangeConstraint::ideal_only($value.to_owned()));
226                 let json = serde_json::json!({
227                     "ideal": $value,
228                 });
229 
230                 test_serde_symmetry!(subject: subject, json: json);
231             }
232         };
233     }
234 
235     mod f64 {
236         use super::*;
237 
238         test_serde!(f64 => {
239             value: 42.0
240         });
241     }
242 
243     mod u64 {
244         use super::*;
245 
246         test_serde!(u64 => {
247             value: 42
248         });
249     }
250 }
251