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, Eq, 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::default().ideal(bare) 68 } 69 MediaTrackConstraintResolutionStrategy::BareToExact => { 70 ValueRangeConstraint::default().exact(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, Eq, 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 #[inline] 122 pub fn exact<U>(mut self, exact: U) -> Self 123 where 124 Option<T>: From<U>, 125 { 126 self.exact = exact.into(); 127 self 128 } 129 130 #[inline] 131 pub fn ideal<U>(mut self, ideal: U) -> Self 132 where 133 Option<T>: From<U>, 134 { 135 self.ideal = ideal.into(); 136 self 137 } 138 139 #[inline] 140 pub fn min<U>(mut self, min: U) -> Self 141 where 142 Option<T>: From<U>, 143 { 144 self.min = min.into(); 145 self 146 } 147 148 #[inline] 149 pub fn max<U>(mut self, max: U) -> Self 150 where 151 Option<T>: From<U>, 152 { 153 self.max = max.into(); 154 self 155 } 156 157 pub fn is_required(&self) -> bool { 158 self.min.is_some() || self.max.is_some() || self.exact.is_some() 159 } 160 161 pub fn is_empty(&self) -> bool { 162 self.min.is_none() && self.max.is_none() && self.exact.is_none() && self.ideal.is_none() 163 } 164 165 pub fn to_required_only(&self) -> Self 166 where 167 T: Clone, 168 { 169 self.clone().into_required_only() 170 } 171 172 pub fn into_required_only(self) -> Self { 173 Self { 174 min: self.min, 175 max: self.max, 176 exact: self.exact, 177 ideal: None, 178 } 179 } 180 } 181 182 impl<T> Default for ValueRangeConstraint<T> { 183 #[inline] 184 fn default() -> Self { 185 Self { 186 min: None, 187 max: None, 188 exact: None, 189 ideal: None, 190 } 191 } 192 } 193 194 impl<T> std::fmt::Display for ValueRangeConstraint<T> 195 where 196 T: std::fmt::Debug, 197 { 198 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 199 let mut is_first = true; 200 f.write_str("(")?; 201 if let Some(exact) = &self.exact { 202 f.write_fmt(format_args!("x == {:?}", exact))?; 203 is_first = false; 204 } else if let (Some(min), Some(max)) = (&self.min, &self.max) { 205 f.write_fmt(format_args!("{:?} <= x <= {:?}", min, max))?; 206 is_first = false; 207 } else if let Some(min) = &self.min { 208 f.write_fmt(format_args!("{:?} <= x", min))?; 209 is_first = false; 210 } else if let Some(max) = &self.max { 211 f.write_fmt(format_args!("x <= {:?}", max))?; 212 is_first = false; 213 } 214 if let Some(ideal) = &self.ideal { 215 if !is_first { 216 f.write_str(" && ")?; 217 } 218 f.write_fmt(format_args!("x~={:?}", ideal))?; 219 is_first = false; 220 } 221 if is_first { 222 f.write_str("<empty>")?; 223 } 224 f.write_str(")")?; 225 Ok(()) 226 } 227 } 228 229 #[cfg(test)] 230 mod tests { 231 use super::*; 232 233 #[test] 234 fn resolve_to_advanced() { 235 let constraint = BareOrValueRangeConstraint::Bare(42); 236 let strategy = MediaTrackConstraintResolutionStrategy::BareToExact; 237 let actual: ValueRangeConstraint<u64> = constraint.into_resolved(strategy); 238 let expected = ValueRangeConstraint::default().exact(42); 239 240 assert_eq!(actual, expected); 241 } 242 243 #[test] 244 fn resolve_to_basic() { 245 let constraint = BareOrValueRangeConstraint::Bare(42); 246 let strategy = MediaTrackConstraintResolutionStrategy::BareToIdeal; 247 let actual: ValueRangeConstraint<u64> = constraint.into_resolved(strategy); 248 let expected = ValueRangeConstraint::default().ideal(42); 249 250 assert_eq!(actual, expected); 251 } 252 } 253 254 #[cfg(feature = "serde")] 255 #[cfg(test)] 256 mod serde_tests { 257 use crate::macros::test_serde_symmetry; 258 259 use super::*; 260 261 macro_rules! test_serde { 262 ($t:ty => { 263 value: $value:expr 264 }) => { 265 type Subject = BareOrValueRangeConstraint<$t>; 266 267 #[test] 268 fn default() { 269 let subject = Subject::default(); 270 let json = serde_json::json!({}); 271 272 test_serde_symmetry!(subject: subject, json: json); 273 } 274 275 #[test] 276 fn bare() { 277 let subject = Subject::Bare($value.to_owned()); 278 let json = serde_json::json!($value); 279 280 test_serde_symmetry!(subject: subject, json: json); 281 } 282 283 #[test] 284 fn min_constraint() { 285 let subject = Subject::Constraint(ValueRangeConstraint::default().min($value.to_owned())); 286 let json = serde_json::json!({ 287 "min": $value, 288 }); 289 290 test_serde_symmetry!(subject: subject, json: json); 291 } 292 293 #[test] 294 fn max_constraint() { 295 let subject = Subject::Constraint(ValueRangeConstraint::default().max($value.to_owned())); 296 let json = serde_json::json!({ 297 "max": $value, 298 }); 299 300 test_serde_symmetry!(subject: subject, json: json); 301 } 302 303 #[test] 304 fn exact_constraint() { 305 let subject = Subject::Constraint(ValueRangeConstraint::default().exact($value.to_owned())); 306 let json = serde_json::json!({ 307 "exact": $value, 308 }); 309 310 test_serde_symmetry!(subject: subject, json: json); 311 } 312 313 #[test] 314 fn ideal_constraint() { 315 let subject = Subject::Constraint(ValueRangeConstraint::default().ideal($value.to_owned())); 316 let json = serde_json::json!({ 317 "ideal": $value, 318 }); 319 320 test_serde_symmetry!(subject: subject, json: json); 321 } 322 323 #[test] 324 fn full_constraint() { 325 let subject = Subject::Constraint(ValueRangeConstraint::default().min($value.to_owned()).max($value.to_owned()).exact($value.to_owned()).ideal($value.to_owned())); 326 let json = serde_json::json!({ 327 "min": $value, 328 "max": $value, 329 "exact": $value, 330 "ideal": $value, 331 }); 332 333 test_serde_symmetry!(subject: subject, json: json); 334 } 335 }; 336 } 337 338 mod f64 { 339 use super::*; 340 341 test_serde!(f64 => { 342 value: 42.0 343 }); 344 } 345 346 mod u64 { 347 use super::*; 348 349 test_serde!(u64 => { 350 value: 42 351 }); 352 } 353 } 354