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