1 use std::ops::Deref; 2 3 #[cfg(feature = "serde")] 4 use serde::{Deserialize, Serialize}; 5 6 pub use self::{ 7 value::{BareOrValueConstraint, ValueConstraint}, 8 value_range::{BareOrValueRangeConstraint, ValueRangeConstraint}, 9 value_sequence::{BareOrValueSequenceConstraint, ValueSequenceConstraint}, 10 }; 11 12 mod value; 13 mod value_range; 14 mod value_sequence; 15 16 /// An empty [constraint][media_track_constraints] value for a [`MediaStreamTrack`][media_stream_track] object. 17 /// 18 /// # W3C Spec Compliance 19 /// 20 /// There exists no corresponding type in the W3C ["Media Capture and Streams"][media_capture_and_streams_spec] spec. 21 /// 22 /// The purpose of this type is to reduce parsing ambiguity, since all constraint variant types 23 /// support serializing from an empty map, but an empty map isn't typed, really, 24 /// so parsing to a specifically typed constraint would be wrong, type-wise. 25 /// 26 /// [media_stream_track]: https://www.w3.org/TR/mediacapture-streams/#dom-mediastreamtrack 27 /// [media_track_constraints]: https://www.w3.org/TR/mediacapture-streams/#dom-mediatrackconstraints 28 /// [media_capture_and_streams_spec]: https://www.w3.org/TR/mediacapture-streams 29 #[derive(Debug, Clone, PartialEq)] 30 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 31 #[cfg_attr(feature = "serde", serde(deny_unknown_fields))] 32 pub struct EmptyConstraint {} 33 34 /// The strategy of a track [constraint][constraint]. 35 /// 36 /// [constraint]: https://www.w3.org/TR/mediacapture-streams/#dfn-constraint 37 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] 38 pub enum MediaTrackConstraintResolutionStrategy { 39 /// Resolve bare values to `ideal` constraints. 40 BareToIdeal, 41 /// Resolve bare values to `exact` constraints. 42 BareToExact, 43 } 44 45 /// A single [constraint][media_track_constraints] value for a [`MediaStreamTrack`][media_stream_track] object. 46 /// 47 /// # W3C Spec Compliance 48 /// 49 /// There exists no corresponding type in the W3C ["Media Capture and Streams"][media_capture_and_streams_spec] spec. 50 /// 51 /// [media_stream_track]: https://www.w3.org/TR/mediacapture-streams/#dom-mediastreamtrack 52 /// [media_track_constraints]: https://www.w3.org/TR/mediacapture-streams/#dom-mediatrackconstraints 53 /// [media_capture_and_streams_spec]: https://www.w3.org/TR/mediacapture-streams 54 #[derive(Debug, Clone, PartialEq)] 55 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 56 #[cfg_attr(feature = "serde", serde(untagged))] 57 pub enum BareOrMediaTrackConstraint { 58 Empty(EmptyConstraint), 59 // `IntegerRange` must be ordered before `FloatRange(…)` in order for 60 // `serde` to decode the correct variant. 61 IntegerRange(BareOrValueRangeConstraint<u64>), 62 FloatRange(BareOrValueRangeConstraint<f64>), 63 // `Bool` must be ordered after `IntegerRange(…)`/`FloatRange(…)` in order for 64 // `serde` to decode the correct variant. 65 Bool(BareOrValueConstraint<bool>), 66 // `StringSequence` must be ordered before `String(…)` in order for 67 // `serde` to decode the correct variant. 68 StringSequence(BareOrValueSequenceConstraint<String>), 69 String(BareOrValueConstraint<String>), 70 } 71 72 impl Default for BareOrMediaTrackConstraint { 73 fn default() -> Self { 74 Self::Empty(EmptyConstraint {}) 75 } 76 } 77 78 // Bool constraint: 79 80 impl From<bool> for BareOrMediaTrackConstraint { 81 fn from(bare: bool) -> Self { 82 Self::Bool(bare.into()) 83 } 84 } 85 86 impl From<ValueConstraint<bool>> for BareOrMediaTrackConstraint { 87 fn from(constraint: ValueConstraint<bool>) -> Self { 88 Self::Bool(constraint.into()) 89 } 90 } 91 92 impl From<BareOrValueConstraint<bool>> for BareOrMediaTrackConstraint { 93 fn from(constraint: BareOrValueConstraint<bool>) -> Self { 94 Self::Bool(constraint) 95 } 96 } 97 98 // Unsigned integer range constraint: 99 100 impl From<u64> for BareOrMediaTrackConstraint { 101 fn from(bare: u64) -> Self { 102 Self::IntegerRange(bare.into()) 103 } 104 } 105 106 impl From<ValueRangeConstraint<u64>> for BareOrMediaTrackConstraint { 107 fn from(constraint: ValueRangeConstraint<u64>) -> Self { 108 Self::IntegerRange(constraint.into()) 109 } 110 } 111 112 impl From<BareOrValueRangeConstraint<u64>> for BareOrMediaTrackConstraint { 113 fn from(constraint: BareOrValueRangeConstraint<u64>) -> Self { 114 Self::IntegerRange(constraint) 115 } 116 } 117 118 // Floating-point range constraint: 119 120 impl From<f64> for BareOrMediaTrackConstraint { 121 fn from(bare: f64) -> Self { 122 Self::FloatRange(bare.into()) 123 } 124 } 125 126 impl From<ValueRangeConstraint<f64>> for BareOrMediaTrackConstraint { 127 fn from(constraint: ValueRangeConstraint<f64>) -> Self { 128 Self::FloatRange(constraint.into()) 129 } 130 } 131 132 impl From<BareOrValueRangeConstraint<f64>> for BareOrMediaTrackConstraint { 133 fn from(constraint: BareOrValueRangeConstraint<f64>) -> Self { 134 Self::FloatRange(constraint) 135 } 136 } 137 138 // String sequence constraint: 139 140 impl From<Vec<String>> for BareOrMediaTrackConstraint { 141 fn from(bare: Vec<String>) -> Self { 142 Self::StringSequence(bare.into()) 143 } 144 } 145 146 impl From<Vec<&str>> for BareOrMediaTrackConstraint { 147 fn from(bare: Vec<&str>) -> Self { 148 let bare: Vec<String> = bare.into_iter().map(|c| c.to_owned()).collect(); 149 Self::from(bare) 150 } 151 } 152 153 impl From<ValueSequenceConstraint<String>> for BareOrMediaTrackConstraint { 154 fn from(constraint: ValueSequenceConstraint<String>) -> Self { 155 Self::StringSequence(constraint.into()) 156 } 157 } 158 159 impl From<BareOrValueSequenceConstraint<String>> for BareOrMediaTrackConstraint { 160 fn from(constraint: BareOrValueSequenceConstraint<String>) -> Self { 161 Self::StringSequence(constraint) 162 } 163 } 164 165 // String constraint: 166 167 impl From<String> for BareOrMediaTrackConstraint { 168 fn from(bare: String) -> Self { 169 Self::String(bare.into()) 170 } 171 } 172 173 impl<'a> From<&'a str> for BareOrMediaTrackConstraint { 174 fn from(bare: &'a str) -> Self { 175 let bare: String = bare.to_owned(); 176 Self::from(bare) 177 } 178 } 179 180 impl From<ValueConstraint<String>> for BareOrMediaTrackConstraint { 181 fn from(constraint: ValueConstraint<String>) -> Self { 182 Self::String(constraint.into()) 183 } 184 } 185 186 impl From<BareOrValueConstraint<String>> for BareOrMediaTrackConstraint { 187 fn from(constraint: BareOrValueConstraint<String>) -> Self { 188 Self::String(constraint) 189 } 190 } 191 192 impl BareOrMediaTrackConstraint { 193 pub fn is_empty(&self) -> bool { 194 match self { 195 Self::Empty(_) => true, 196 Self::IntegerRange(constraint) => constraint.is_empty(), 197 Self::FloatRange(constraint) => constraint.is_empty(), 198 Self::Bool(constraint) => constraint.is_empty(), 199 Self::StringSequence(constraint) => constraint.is_empty(), 200 Self::String(constraint) => constraint.is_empty(), 201 } 202 } 203 204 pub fn to_resolved( 205 &self, 206 strategy: MediaTrackConstraintResolutionStrategy, 207 ) -> MediaTrackConstraint { 208 self.clone().into_resolved(strategy) 209 } 210 211 pub fn into_resolved( 212 self, 213 strategy: MediaTrackConstraintResolutionStrategy, 214 ) -> MediaTrackConstraint { 215 match self { 216 Self::Empty(constraint) => MediaTrackConstraint::Empty(constraint), 217 Self::IntegerRange(constraint) => { 218 MediaTrackConstraint::IntegerRange(constraint.into_resolved(strategy)) 219 } 220 Self::FloatRange(constraint) => { 221 MediaTrackConstraint::FloatRange(constraint.into_resolved(strategy)) 222 } 223 Self::Bool(constraint) => { 224 MediaTrackConstraint::Bool(constraint.into_resolved(strategy)) 225 } 226 Self::StringSequence(constraint) => { 227 MediaTrackConstraint::StringSequence(constraint.into_resolved(strategy)) 228 } 229 Self::String(constraint) => { 230 MediaTrackConstraint::String(constraint.into_resolved(strategy)) 231 } 232 } 233 } 234 } 235 236 /// A single [constraint][media_track_constraints] value for a [`MediaStreamTrack`][media_stream_track] object 237 /// with its potential bare value either resolved to an `exact` or `ideal` constraint. 238 /// 239 /// # W3C Spec Compliance 240 /// 241 /// There exists no corresponding type in the W3C ["Media Capture and Streams"][media_capture_and_streams_spec] spec. 242 /// 243 /// [media_stream_track]: https://www.w3.org/TR/mediacapture-streams/#dom-mediastreamtrack 244 /// [media_track_constraints]: https://www.w3.org/TR/mediacapture-streams/#dom-mediatrackconstraints 245 /// [media_capture_and_streams_spec]: https://www.w3.org/TR/mediacapture-streams 246 #[derive(Debug, Clone, PartialEq)] 247 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 248 #[cfg_attr(feature = "serde", serde(untagged))] 249 pub enum MediaTrackConstraint { 250 Empty(EmptyConstraint), 251 IntegerRange(ValueRangeConstraint<u64>), 252 FloatRange(ValueRangeConstraint<f64>), 253 Bool(ValueConstraint<bool>), 254 StringSequence(ValueSequenceConstraint<String>), 255 String(ValueConstraint<String>), 256 } 257 258 impl Default for MediaTrackConstraint { 259 fn default() -> Self { 260 Self::Empty(EmptyConstraint {}) 261 } 262 } 263 264 impl MediaTrackConstraint { 265 pub fn is_required(&self) -> bool { 266 match self { 267 Self::Empty(_constraint) => false, 268 Self::IntegerRange(constraint) => constraint.is_required(), 269 Self::FloatRange(constraint) => constraint.is_required(), 270 Self::Bool(constraint) => constraint.is_required(), 271 Self::StringSequence(constraint) => constraint.is_required(), 272 Self::String(constraint) => constraint.is_required(), 273 } 274 } 275 276 pub fn is_empty(&self) -> bool { 277 match self { 278 Self::Empty(_constraint) => true, 279 Self::IntegerRange(constraint) => constraint.is_empty(), 280 Self::FloatRange(constraint) => constraint.is_empty(), 281 Self::Bool(constraint) => constraint.is_empty(), 282 Self::StringSequence(constraint) => constraint.is_empty(), 283 Self::String(constraint) => constraint.is_empty(), 284 } 285 } 286 287 pub fn to_sanitized(&self) -> Option<SanitizedMediaTrackConstraint> { 288 self.clone().into_sanitized() 289 } 290 291 pub fn into_sanitized(self) -> Option<SanitizedMediaTrackConstraint> { 292 if self.is_empty() { 293 return None; 294 } 295 296 Some(SanitizedMediaTrackConstraint(self)) 297 } 298 } 299 300 /// A single non-empty [constraint][media_track_constraints] value for a [`MediaStreamTrack`][media_stream_track] object. 301 /// 302 /// # Invariant 303 /// 304 /// The wrapped `MediaTrackConstraint` MUST not be empty. 305 /// 306 /// To enforce this invariant the only way to create an instance of this type 307 /// is by calling `constraint.to_sanitized()`/`constraint.into_sanitized()` on 308 /// an instance of `MediaTrackConstraint`, which returns `None` if `self` is empty. 309 /// 310 /// Further more `self.0` MUST NOT be exposed mutably, 311 /// as otherwise it could become empty via mutation. 312 #[derive(Debug, Clone, PartialEq)] 313 pub struct SanitizedMediaTrackConstraint(MediaTrackConstraint); 314 315 impl Deref for SanitizedMediaTrackConstraint { 316 type Target = MediaTrackConstraint; 317 318 fn deref(&self) -> &Self::Target { 319 &self.0 320 } 321 } 322 323 impl SanitizedMediaTrackConstraint { 324 pub fn into_inner(self) -> MediaTrackConstraint { 325 self.0 326 } 327 328 pub fn integer_range(&self) -> Option<&ValueRangeConstraint<u64>> { 329 if let MediaTrackConstraint::IntegerRange(constraint) = &self.0 { 330 Some(constraint) 331 } else { 332 None 333 } 334 } 335 336 pub fn float_range(&self) -> Option<&ValueRangeConstraint<f64>> { 337 if let MediaTrackConstraint::FloatRange(constraint) = &self.0 { 338 Some(constraint) 339 } else { 340 None 341 } 342 } 343 344 pub fn bool(&self) -> Option<&ValueConstraint<bool>> { 345 if let MediaTrackConstraint::Bool(constraint) = &self.0 { 346 Some(constraint) 347 } else { 348 None 349 } 350 } 351 352 pub fn string_sequence(&self) -> Option<&ValueSequenceConstraint<String>> { 353 if let MediaTrackConstraint::StringSequence(constraint) = &self.0 { 354 Some(constraint) 355 } else { 356 None 357 } 358 } 359 360 pub fn string(&self) -> Option<&ValueConstraint<String>> { 361 if let MediaTrackConstraint::String(constraint) = &self.0 { 362 Some(constraint) 363 } else { 364 None 365 } 366 } 367 } 368 369 #[cfg(feature = "serde")] 370 #[cfg(test)] 371 mod serde_tests { 372 use crate::macros::test_serde_symmetry; 373 374 use super::*; 375 376 type Subject = BareOrMediaTrackConstraint; 377 378 #[test] 379 fn empty() { 380 let subject = Subject::Empty(EmptyConstraint {}); 381 let json = serde_json::json!({}); 382 383 test_serde_symmetry!(subject: subject, json: json); 384 } 385 386 #[test] 387 fn bool_bare() { 388 let subject = Subject::Bool(true.into()); 389 let json = serde_json::json!(true); 390 391 test_serde_symmetry!(subject: subject, json: json); 392 } 393 394 #[test] 395 fn bool_constraint() { 396 let subject = Subject::Bool(ValueConstraint::exact_only(true).into()); 397 let json = serde_json::json!({ "exact": true }); 398 399 test_serde_symmetry!(subject: subject, json: json); 400 } 401 402 #[test] 403 fn integer_range_bare() { 404 let subject = Subject::IntegerRange(42.into()); 405 let json = serde_json::json!(42); 406 407 test_serde_symmetry!(subject: subject, json: json); 408 } 409 410 #[test] 411 fn integer_range_constraint() { 412 let subject = Subject::IntegerRange(ValueRangeConstraint::exact_only(42).into()); 413 let json = serde_json::json!({ "exact": 42 }); 414 415 test_serde_symmetry!(subject: subject, json: json); 416 } 417 418 #[test] 419 fn float_range_bare() { 420 let subject = Subject::FloatRange(4.2.into()); 421 let json = serde_json::json!(4.2); 422 423 test_serde_symmetry!(subject: subject, json: json); 424 } 425 426 #[test] 427 fn float_range_constraint() { 428 let subject = Subject::FloatRange(ValueRangeConstraint::exact_only(42.0).into()); 429 let json = serde_json::json!({ "exact": 42.0 }); 430 431 test_serde_symmetry!(subject: subject, json: json); 432 } 433 434 #[test] 435 fn string_sequence_bare() { 436 let subject = Subject::StringSequence(vec!["foo".to_owned(), "bar".to_owned()].into()); 437 let json = serde_json::json!(["foo", "bar"]); 438 439 test_serde_symmetry!(subject: subject, json: json); 440 } 441 442 #[test] 443 fn string_sequence_constraint() { 444 let subject = Subject::StringSequence( 445 ValueSequenceConstraint::exact_only(vec!["foo".to_owned(), "bar".to_owned()].into()) 446 .into(), 447 ); 448 let json = serde_json::json!({ "exact": ["foo", "bar"] }); 449 450 test_serde_symmetry!(subject: subject, json: json); 451 } 452 453 #[test] 454 fn string_bare() { 455 let subject = Subject::String("foo".to_owned().into()); 456 let json = serde_json::json!("foo"); 457 458 test_serde_symmetry!(subject: subject, json: json); 459 } 460 461 #[test] 462 fn string_constraint() { 463 let subject = Subject::String(ValueConstraint::exact_only("foo".to_owned()).into()); 464 let json = serde_json::json!({ "exact": "foo" }); 465 466 test_serde_symmetry!(subject: subject, json: json); 467 } 468 } 469