webrtc_constraints/capability/
value_range.rs

1use std::ops::{RangeFrom, RangeInclusive, RangeToInclusive};
2
3#[cfg(feature = "serde")]
4use 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"))]
26pub 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
39impl<T> Default for MediaTrackValueRangeCapability<T> {
40    fn default() -> Self {
41        Self {
42            min: Default::default(),
43            max: Default::default(),
44        }
45    }
46}
47
48impl<T> From<RangeInclusive<T>> for MediaTrackValueRangeCapability<T> {
49    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
58impl<T> From<RangeFrom<T>> for MediaTrackValueRangeCapability<T> {
59    fn from(range: RangeFrom<T>) -> Self {
60        Self {
61            min: Some(range.start),
62            max: None,
63        }
64    }
65}
66
67impl<T> From<RangeToInclusive<T>> for MediaTrackValueRangeCapability<T> {
68    fn from(range: RangeToInclusive<T>) -> Self {
69        Self {
70            min: None,
71            max: Some(range.end),
72        }
73    }
74}
75
76impl<T> MediaTrackValueRangeCapability<T> {
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)]
100mod tests {
101    use super::*;
102
103    type Subject = MediaTrackValueRangeCapability<i64>;
104
105    #[test]
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]
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]
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]
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]
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]
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]
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]
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)]
188mod serde_tests {
189    use crate::macros::test_serde_symmetry;
190
191    use super::*;
192
193    type Subject = MediaTrackValueRangeCapability<i64>;
194
195    #[test]
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}