1 #[cfg(feature = "serde")] 2 use serde::{Deserialize, Serialize}; 3 4 use crate::MediaTrackConstraintResolutionStrategy; 5 6 /// A bare value or constraint specifying a sequence 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 `ValueConstraint<T>` type aims to be a generalization over 13 /// multiple types in the spec. 14 /// 15 /// | Rust | W3C | 16 /// | ---------------------------------------- | -------------------------------------------- | 17 /// | `ValueSequenceConstraint<String>` | [`ConstrainDOMString`][constrain_dom_string] | 18 /// 19 /// [constrain_dom_string]: https://www.w3.org/TR/mediacapture-streams/#dom-constraindomstring 20 /// [media_capture_and_streams_spec]: https://www.w3.org/TR/mediacapture-streams/ 21 #[derive(Debug, Clone, Eq, PartialEq)] 22 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 23 #[cfg_attr(feature = "serde", serde(untagged))] 24 pub enum ValueSequenceConstraint<T> { 25 /// A bare-valued media track constraint. 26 Bare(Vec<T>), 27 /// A fully-qualified media track constraint. 28 Constraint(ResolvedValueSequenceConstraint<T>), 29 } 30 31 impl<T> Default for ValueSequenceConstraint<T> { default() -> Self32 fn default() -> Self { 33 Self::Constraint(Default::default()) 34 } 35 } 36 37 impl<T> From<T> for ValueSequenceConstraint<T> { from(bare: T) -> Self38 fn from(bare: T) -> Self { 39 Self::Bare(vec![bare]) 40 } 41 } 42 43 impl<T> From<Vec<T>> for ValueSequenceConstraint<T> { from(bare: Vec<T>) -> Self44 fn from(bare: Vec<T>) -> Self { 45 Self::Bare(bare) 46 } 47 } 48 49 impl<T> From<ResolvedValueSequenceConstraint<T>> for ValueSequenceConstraint<T> { from(constraint: ResolvedValueSequenceConstraint<T>) -> Self50 fn from(constraint: ResolvedValueSequenceConstraint<T>) -> Self { 51 Self::Constraint(constraint) 52 } 53 } 54 55 impl<T> ValueSequenceConstraint<T> 56 where 57 T: Clone, 58 { 59 /// Returns a resolved representation of the constraint 60 /// with bare values resolved to fully-qualified constraints. to_resolved( &self, strategy: MediaTrackConstraintResolutionStrategy, ) -> ResolvedValueSequenceConstraint<T>61 pub fn to_resolved( 62 &self, 63 strategy: MediaTrackConstraintResolutionStrategy, 64 ) -> ResolvedValueSequenceConstraint<T> { 65 self.clone().into_resolved(strategy) 66 } 67 68 /// Consumes the constraint, returning a resolved representation of the 69 /// constraint with bare values resolved to fully-qualified constraints. into_resolved( self, strategy: MediaTrackConstraintResolutionStrategy, ) -> ResolvedValueSequenceConstraint<T>70 pub fn into_resolved( 71 self, 72 strategy: MediaTrackConstraintResolutionStrategy, 73 ) -> ResolvedValueSequenceConstraint<T> { 74 match self { 75 Self::Bare(bare) => match strategy { 76 MediaTrackConstraintResolutionStrategy::BareToIdeal => { 77 ResolvedValueSequenceConstraint::default().ideal(bare) 78 } 79 MediaTrackConstraintResolutionStrategy::BareToExact => { 80 ResolvedValueSequenceConstraint::default().exact(bare) 81 } 82 }, 83 Self::Constraint(constraint) => constraint, 84 } 85 } 86 } 87 88 impl<T> ValueSequenceConstraint<T> { 89 /// Returns `true` if `self` is empty, otherwise `false`. is_empty(&self) -> bool90 pub fn is_empty(&self) -> bool { 91 match self { 92 Self::Bare(bare) => bare.is_empty(), 93 Self::Constraint(constraint) => constraint.is_empty(), 94 } 95 } 96 } 97 98 /// A constraint specifying a sequence of accepted values. 99 /// 100 /// # W3C Spec Compliance 101 /// 102 /// There exists no direct corresponding type in the 103 /// W3C ["Media Capture and Streams"][media_capture_and_streams_spec] spec, 104 /// since the `ValueSequenceConstraint<T>` type aims to be a 105 /// generalization over multiple types in the W3C spec: 106 /// 107 /// | Rust | W3C | 108 /// | --------------------------------- | ----------------------------------------------------------------- | 109 /// | `ResolvedValueSequenceConstraint<String>` | [`ConstrainDOMStringParameters`][constrain_dom_string_parameters] | 110 /// 111 /// [constrain_dom_string_parameters]: https://www.w3.org/TR/mediacapture-streams/#dom-constraindomstringparameters 112 /// [media_capture_and_streams_spec]: https://www.w3.org/TR/mediacapture-streams/ 113 #[derive(Debug, Clone, Eq, PartialEq)] 114 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 115 #[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] 116 pub struct ResolvedValueSequenceConstraint<T> { 117 /// The exact required value for this property. 118 /// 119 /// This is a required value. 120 #[cfg_attr( 121 feature = "serde", 122 serde(skip_serializing_if = "core::option::Option::is_none") 123 )] 124 pub exact: Option<Vec<T>>, 125 /// The ideal (target) value for this property. 126 /// 127 /// This is an optional value. 128 #[cfg_attr( 129 feature = "serde", 130 serde(skip_serializing_if = "core::option::Option::is_none") 131 )] 132 pub ideal: Option<Vec<T>>, 133 } 134 135 impl<T> ResolvedValueSequenceConstraint<T> { 136 /// Consumes `self`, returning a corresponding constraint 137 /// with the exact required value set to `exact`. 138 #[inline] exact<U>(mut self, exact: U) -> Self where Option<Vec<T>>: From<U>,139 pub fn exact<U>(mut self, exact: U) -> Self 140 where 141 Option<Vec<T>>: From<U>, 142 { 143 self.exact = exact.into(); 144 self 145 } 146 147 /// Consumes `self`, returning a corresponding constraint 148 /// with the ideal required value set to `ideal`. 149 #[inline] ideal<U>(mut self, ideal: U) -> Self where Option<Vec<T>>: From<U>,150 pub fn ideal<U>(mut self, ideal: U) -> Self 151 where 152 Option<Vec<T>>: From<U>, 153 { 154 self.ideal = ideal.into(); 155 self 156 } 157 158 /// Returns `true` if `value.is_some()` is `true` for any of its required values, 159 /// otherwise `false`. is_required(&self) -> bool160 pub fn is_required(&self) -> bool { 161 self.exact.is_some() 162 } 163 164 /// Returns `true` if `value.is_none()` is `true` for all of its values, 165 /// otherwise `false`. is_empty(&self) -> bool166 pub fn is_empty(&self) -> bool { 167 let exact_is_empty = self.exact.as_ref().map_or(true, Vec::is_empty); 168 let ideal_is_empty = self.ideal.as_ref().map_or(true, Vec::is_empty); 169 exact_is_empty && ideal_is_empty 170 } 171 172 /// Returns a corresponding constraint containing only required values. to_required_only(&self) -> Self where T: Clone,173 pub fn to_required_only(&self) -> Self 174 where 175 T: Clone, 176 { 177 self.clone().into_required_only() 178 } 179 180 /// Consumes `self, returning a corresponding constraint 181 /// containing only required values. into_required_only(self) -> Self182 pub fn into_required_only(self) -> Self { 183 Self { 184 exact: self.exact, 185 ideal: None, 186 } 187 } 188 } 189 190 impl<T> Default for ResolvedValueSequenceConstraint<T> { default() -> Self191 fn default() -> Self { 192 Self { 193 exact: None, 194 ideal: None, 195 } 196 } 197 } 198 199 impl<T> std::fmt::Display for ResolvedValueSequenceConstraint<T> 200 where 201 T: std::fmt::Debug, 202 { fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result203 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 204 let mut is_first = true; 205 f.write_str("(")?; 206 if let Some(ref exact) = &self.exact { 207 f.write_fmt(format_args!("x == {exact:?}"))?; 208 is_first = false; 209 } 210 if let Some(ref ideal) = &self.ideal { 211 if !is_first { 212 f.write_str(" && ")?; 213 } 214 f.write_fmt(format_args!("x ~= {ideal:?}"))?; 215 is_first = false; 216 } 217 if is_first { 218 f.write_str("<empty>")?; 219 } 220 f.write_str(")")?; 221 Ok(()) 222 } 223 } 224 225 #[cfg(test)] 226 mod tests { 227 use super::*; 228 229 #[test] to_string()230 fn to_string() { 231 let scenarios = [ 232 (ResolvedValueSequenceConstraint::default(), "(<empty>)"), 233 ( 234 ResolvedValueSequenceConstraint::default().exact(vec![1, 2]), 235 "(x == [1, 2])", 236 ), 237 ( 238 ResolvedValueSequenceConstraint::default().ideal(vec![2, 3]), 239 "(x ~= [2, 3])", 240 ), 241 ( 242 ResolvedValueSequenceConstraint::default() 243 .exact(vec![1, 2]) 244 .ideal(vec![2, 3]), 245 "(x == [1, 2] && x ~= [2, 3])", 246 ), 247 ]; 248 249 for (constraint, expected) in scenarios { 250 let actual = constraint.to_string(); 251 252 assert_eq!(actual, expected); 253 } 254 } 255 256 #[test] is_required()257 fn is_required() { 258 let scenarios = [ 259 (ResolvedValueSequenceConstraint::default(), false), 260 ( 261 ResolvedValueSequenceConstraint::default().exact(vec![true]), 262 true, 263 ), 264 ( 265 ResolvedValueSequenceConstraint::default().ideal(vec![true]), 266 false, 267 ), 268 ( 269 ResolvedValueSequenceConstraint::default() 270 .exact(vec![true]) 271 .ideal(vec![true]), 272 true, 273 ), 274 ]; 275 276 for (constraint, expected) in scenarios { 277 let actual = constraint.is_required(); 278 279 assert_eq!(actual, expected); 280 } 281 } 282 283 mod is_empty { 284 use super::*; 285 286 #[test] bare()287 fn bare() { 288 let constraint = ValueSequenceConstraint::Bare(vec![true]); 289 290 assert!(!constraint.is_empty()); 291 } 292 293 #[test] constraint()294 fn constraint() { 295 let scenarios = [ 296 (ResolvedValueSequenceConstraint::default(), true), 297 ( 298 ResolvedValueSequenceConstraint::default().exact(vec![true]), 299 false, 300 ), 301 ( 302 ResolvedValueSequenceConstraint::default().ideal(vec![true]), 303 false, 304 ), 305 ( 306 ResolvedValueSequenceConstraint::default() 307 .exact(vec![true]) 308 .ideal(vec![true]), 309 false, 310 ), 311 ]; 312 313 for (constraint, expected) in scenarios { 314 let constraint = ValueSequenceConstraint::<bool>::Constraint(constraint); 315 316 let actual = constraint.is_empty(); 317 318 assert_eq!(actual, expected); 319 } 320 } 321 } 322 323 #[test] resolve_to_advanced()324 fn resolve_to_advanced() { 325 let constraints = [ 326 ValueSequenceConstraint::Bare(vec![true]), 327 ValueSequenceConstraint::Constraint( 328 ResolvedValueSequenceConstraint::default().exact(vec![true]), 329 ), 330 ]; 331 let strategy = MediaTrackConstraintResolutionStrategy::BareToExact; 332 333 for constraint in constraints { 334 let actuals = [ 335 constraint.to_resolved(strategy), 336 constraint.into_resolved(strategy), 337 ]; 338 339 let expected = ResolvedValueSequenceConstraint::default().exact(vec![true]); 340 341 for actual in actuals { 342 assert_eq!(actual, expected); 343 } 344 } 345 } 346 347 #[test] resolve_to_basic()348 fn resolve_to_basic() { 349 let constraints = [ 350 ValueSequenceConstraint::Bare(vec![true]), 351 ValueSequenceConstraint::Constraint( 352 ResolvedValueSequenceConstraint::default().ideal(vec![true]), 353 ), 354 ]; 355 let strategy = MediaTrackConstraintResolutionStrategy::BareToIdeal; 356 357 for constraint in constraints { 358 let actuals = [ 359 constraint.to_resolved(strategy), 360 constraint.into_resolved(strategy), 361 ]; 362 363 let expected = ResolvedValueSequenceConstraint::default().ideal(vec![true]); 364 365 for actual in actuals { 366 assert_eq!(actual, expected); 367 } 368 } 369 } 370 } 371 372 #[cfg(feature = "serde")] 373 #[cfg(test)] 374 mod serde_tests { 375 use crate::macros::test_serde_symmetry; 376 377 use super::*; 378 379 macro_rules! test_serde { 380 ($t:ty => { 381 values: [$($values:expr),*] 382 }) => { 383 type Subject = ValueSequenceConstraint<$t>; 384 385 #[test] 386 fn default() { 387 let subject = Subject::default(); 388 let json = serde_json::json!({}); 389 390 test_serde_symmetry!(subject: subject, json: json); 391 } 392 393 #[test] 394 fn bare() { 395 let subject = Subject::Bare(vec![$($values.to_owned()),*].into()); 396 let json = serde_json::json!([$($values),*]); 397 398 test_serde_symmetry!(subject: subject, json: json); 399 } 400 401 #[test] 402 fn exact_constraint() { 403 let subject = Subject::Constraint(ResolvedValueSequenceConstraint::default().exact(vec![$($values.to_owned()),*])); 404 let json = serde_json::json!({ 405 "exact": [$($values),*], 406 }); 407 408 test_serde_symmetry!(subject: subject, json: json); 409 } 410 411 #[test] 412 fn ideal_constraint() { 413 let subject = Subject::Constraint(ResolvedValueSequenceConstraint::default().ideal(vec![$($values.to_owned()),*])); 414 let json = serde_json::json!({ 415 "ideal": [$($values),*], 416 }); 417 418 test_serde_symmetry!(subject: subject, json: json); 419 } 420 421 #[test] 422 fn full_constraint() { 423 let subject = Subject::Constraint(ResolvedValueSequenceConstraint::default().exact(vec![$($values.to_owned()),*]).ideal(vec![$($values.to_owned()),*])); 424 let json = serde_json::json!({ 425 "exact": [$($values),*], 426 "ideal": [$($values),*], 427 }); 428 429 test_serde_symmetry!(subject: subject, json: json); 430 } 431 }; 432 } 433 434 mod string { 435 use super::*; 436 437 test_serde!(String => { 438 values: ["VALUE_0", "VALUE_1"] 439 }); 440 } 441 } 442