1 #[cfg(feature = "serde")]
2 use serde::{Deserialize, Serialize};
3 
4 /// A bare value or constraint specifying a range of accepted values.
5 ///
6 /// # W3C Spec Compliance
7 ///
8 /// There exists no direct corresponding type in the
9 /// W3C ["Media Capture and Streams"][media_capture_and_streams_spec] spec,
10 /// since the `BareOrValueConstraint<T>` type aims to be a generalization over
11 /// multiple types in the spec.
12 ///
13 /// | Rust                               | W3C                                   |
14 /// | ---------------------------------- | ------------------------------------- |
15 /// | `BareOrValueRangeConstraint<u64>` | [`ConstrainULong`][constrain_ulong]   |
16 /// | `BareOrValueRangeConstraint<f64>` | [`ConstrainDouble`][constrain_double] |
17 ///
18 /// [constrain_double]: https://www.w3.org/TR/mediacapture-streams/#dom-constraindouble
19 /// [constrain_ulong]: https://www.w3.org/TR/mediacapture-streams/#dom-constrainulong
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 BareOrValueRangeConstraint<T> {
25     Bare(T),
26     Constraint(ValueRangeConstraint<T>),
27 }
28 
29 impl<T> Default for BareOrValueRangeConstraint<T> {
30     fn default() -> Self {
31         Self::Constraint(Default::default())
32     }
33 }
34 
35 impl<T> From<T> for BareOrValueRangeConstraint<T> {
36     fn from(bare: T) -> Self {
37         Self::Bare(bare)
38     }
39 }
40 
41 impl<T> From<ValueRangeConstraint<T>> for BareOrValueRangeConstraint<T> {
42     fn from(constraint: ValueRangeConstraint<T>) -> Self {
43         Self::Constraint(constraint)
44     }
45 }
46 
47 /// A constraint specifying a range of accepted values.
48 ///
49 /// Corresponding W3C spec types as per ["Media Capture and Streams"][spec]:
50 /// - `ConstrainDouble` => `ValueRangeConstraint<f64>`
51 /// - `ConstrainULong` => `ValueRangeConstraint<u64>`
52 ///
53 /// [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(rename_all = "camelCase"))]
57 pub struct ValueRangeConstraint<T> {
58     #[cfg_attr(
59         feature = "serde",
60         serde(skip_serializing_if = "core::option::Option::is_none")
61     )]
62     pub min: Option<T>,
63     #[cfg_attr(
64         feature = "serde",
65         serde(skip_serializing_if = "core::option::Option::is_none")
66     )]
67     pub max: Option<T>,
68     #[cfg_attr(
69         feature = "serde",
70         serde(skip_serializing_if = "core::option::Option::is_none")
71     )]
72     pub exact: Option<T>,
73     #[cfg_attr(
74         feature = "serde",
75         serde(skip_serializing_if = "core::option::Option::is_none")
76     )]
77     pub ideal: Option<T>,
78 }
79 
80 impl<T> ValueRangeConstraint<T> {
81     pub fn exact_only(exact: T) -> Self {
82         Self {
83             min: None,
84             max: None,
85             exact: Some(exact),
86             ideal: None,
87         }
88     }
89 
90     pub fn ideal_only(ideal: T) -> Self {
91         Self {
92             min: None,
93             max: None,
94             exact: None,
95             ideal: Some(ideal),
96         }
97     }
98 
99     pub fn is_required(&self) -> bool {
100         self.min.is_some() || self.max.is_some() || self.exact.is_some()
101     }
102 }
103 
104 impl<T> Default for ValueRangeConstraint<T> {
105     fn default() -> Self {
106         Self {
107             min: None,
108             max: None,
109             exact: None,
110             ideal: None,
111         }
112     }
113 }
114 
115 #[cfg(feature = "serde")]
116 #[cfg(test)]
117 mod serde_tests {
118     use crate::macros::test_serde_symmetry;
119 
120     use super::*;
121 
122     macro_rules! test_serde {
123         ($t:ty => {
124             value: $value:expr
125         }) => {
126             type Subject = BareOrValueRangeConstraint<$t>;
127 
128             #[test]
129             fn default() {
130                 let subject = Subject::default();
131                 let json = serde_json::json!({});
132 
133                 test_serde_symmetry!(subject: subject, json: json);
134             }
135 
136             #[test]
137             fn bare() {
138                 let subject = Subject::Bare($value.to_owned());
139                 let json = serde_json::json!($value);
140 
141                 test_serde_symmetry!(subject: subject, json: json);
142             }
143 
144             #[test]
145             fn exact() {
146                 let subject = Subject::Constraint(ValueRangeConstraint::exact_only($value.to_owned()));
147                 let json = serde_json::json!({
148                     "exact": $value,
149                 });
150 
151                 test_serde_symmetry!(subject: subject, json: json);
152             }
153 
154             #[test]
155             fn ideal() {
156                 let subject = Subject::Constraint(ValueRangeConstraint::ideal_only($value.to_owned()));
157                 let json = serde_json::json!({
158                     "ideal": $value,
159                 });
160 
161                 test_serde_symmetry!(subject: subject, json: json);
162             }
163         };
164     }
165 
166     mod f64 {
167         use super::*;
168 
169         test_serde!(f64 => {
170             value: 42.0
171         });
172     }
173 
174     mod u64 {
175         use super::*;
176 
177         test_serde!(u64 => {
178             value: 42
179         });
180     }
181 }
182