1 use std::ops::{RangeFrom, RangeInclusive, RangeToInclusive};
2 
3 #[cfg(feature = "serde")]
4 use serde::{Deserialize, Serialize};
5 
6 /// A capability specifying a range of supported 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 `MediaTrackValueRangeCapability<T>` type aims to be a
13 /// generalization over multiple types in the W3C spec:
14 ///
15 /// | Rust                                  | W3C                           |
16 /// | ------------------------------------- | ----------------------------- |
17 /// | `MediaTrackValueRangeCapability<u64>` | [`ULongRange`][ulong_range]   |
18 /// | `MediaTrackValueRangeCapability<f64>` | [`DoubleRange`][double_range] |
19 ///
20 /// [double_range]: https://www.w3.org/TR/mediacapture-streams/#dom-doublerange
21 /// [media_capture_and_streams_spec]: https://www.w3.org/TR/mediacapture-streams/
22 /// [ulong_range]: https://www.w3.org/TR/mediacapture-streams/#dom-ulongrange
23 #[derive(Debug, Clone, Eq, PartialEq)]
24 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
25 #[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
26 pub struct MediaTrackValueRangeCapability<T> {
27     #[cfg_attr(
28         feature = "serde",
29         serde(skip_serializing_if = "core::option::Option::is_none")
30     )]
31     pub min: Option<T>,
32     #[cfg_attr(
33         feature = "serde",
34         serde(skip_serializing_if = "core::option::Option::is_none")
35     )]
36     pub max: Option<T>,
37 }
38 
39 impl<T> Default for MediaTrackValueRangeCapability<T> {
default() -> Self40     fn default() -> Self {
41         Self {
42             min: Default::default(),
43             max: Default::default(),
44         }
45     }
46 }
47 
48 impl<T> From<RangeInclusive<T>> for MediaTrackValueRangeCapability<T> {
from(range: RangeInclusive<T>) -> Self49     fn from(range: RangeInclusive<T>) -> Self {
50         let (min, max) = range.into_inner();
51         Self {
52             min: Some(min),
53             max: Some(max),
54         }
55     }
56 }
57 
58 impl<T> From<RangeFrom<T>> for MediaTrackValueRangeCapability<T> {
from(range: RangeFrom<T>) -> Self59     fn from(range: RangeFrom<T>) -> Self {
60         Self {
61             min: Some(range.start),
62             max: None,
63         }
64     }
65 }
66 
67 impl<T> From<RangeToInclusive<T>> for MediaTrackValueRangeCapability<T> {
from(range: RangeToInclusive<T>) -> Self68     fn from(range: RangeToInclusive<T>) -> Self {
69         Self {
70             min: None,
71             max: Some(range.end),
72         }
73     }
74 }
75 
76 impl<T> MediaTrackValueRangeCapability<T> {
contains(&self, value: &T) -> bool where T: PartialOrd,77     pub fn contains(&self, value: &T) -> bool
78     where
79         T: PartialOrd,
80     {
81         // FIXME(regexident): replace with if-let-chain, once stabilized:
82         // Tracking issue: https://github.com/rust-lang/rust/issues/53667
83         if let Some(ref min) = self.min {
84             if min > value {
85                 return false;
86             }
87         }
88         // FIXME(regexident): replace with if-let-chain, once stabilized:
89         // Tracking issue: https://github.com/rust-lang/rust/issues/53667
90         if let Some(ref max) = self.max {
91             if max < value {
92                 return false;
93             }
94         }
95         true
96     }
97 }
98 
99 #[cfg(test)]
100 mod tests {
101     use super::*;
102 
103     type Subject = MediaTrackValueRangeCapability<i64>;
104 
105     #[test]
default()106     fn default() {
107         let subject = Subject::default();
108 
109         assert_eq!(subject.min, None);
110         assert_eq!(subject.max, None);
111     }
112 
113     mod from {
114         use super::*;
115 
116         #[test]
range_inclusive()117         fn range_inclusive() {
118             let subject = Subject::from(1..=5);
119 
120             assert_eq!(subject.min, Some(1));
121             assert_eq!(subject.max, Some(5));
122         }
123 
124         #[test]
range_from()125         fn range_from() {
126             let subject = Subject::from(1..);
127 
128             assert_eq!(subject.min, Some(1));
129             assert_eq!(subject.max, None);
130         }
131 
132         #[test]
range_to_inclusive()133         fn range_to_inclusive() {
134             let subject = Subject::from(..=5);
135 
136             assert_eq!(subject.min, None);
137             assert_eq!(subject.max, Some(5));
138         }
139     }
140 
141     mod contains {
142         use super::*;
143 
144         #[test]
default()145         fn default() {
146             let subject = Subject::default();
147 
148             assert!(subject.contains(&0));
149             assert!(subject.contains(&1));
150             assert!(subject.contains(&5));
151             assert!(subject.contains(&6));
152         }
153 
154         #[test]
from_range_inclusive()155         fn from_range_inclusive() {
156             let subject = Subject::from(1..=5);
157 
158             assert!(!subject.contains(&0));
159             assert!(subject.contains(&1));
160             assert!(subject.contains(&5));
161             assert!(!subject.contains(&6));
162         }
163 
164         #[test]
from_range_from()165         fn from_range_from() {
166             let subject = Subject::from(1..);
167 
168             assert!(!subject.contains(&0));
169             assert!(subject.contains(&1));
170             assert!(subject.contains(&5));
171             assert!(subject.contains(&6));
172         }
173 
174         #[test]
from_range_to_inclusive()175         fn from_range_to_inclusive() {
176             let subject = Subject::from(..=5);
177 
178             assert!(subject.contains(&0));
179             assert!(subject.contains(&1));
180             assert!(subject.contains(&5));
181             assert!(!subject.contains(&6));
182         }
183     }
184 }
185 
186 #[cfg(feature = "serde")]
187 #[cfg(test)]
188 mod serde_tests {
189     use crate::macros::test_serde_symmetry;
190 
191     use super::*;
192 
193     type Subject = MediaTrackValueRangeCapability<i64>;
194 
195     #[test]
customized()196     fn customized() {
197         let subject = Subject {
198             min: Some(12),
199             max: Some(34),
200         };
201         let json = serde_json::json!({
202             "min": 12,
203             "max": 34,
204         });
205 
206         test_serde_symmetry!(subject: subject, json: json);
207     }
208 }
209