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