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