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 `BareOrValueConstraint<T>` type aims to be a generalization over 13 /// multiple types in the spec. 14 /// 15 /// | Rust | W3C | 16 /// | ---------------------------------------- | -------------------------------------------- | 17 /// | `BareOrValueSequenceConstraint<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 BareOrValueSequenceConstraint<T> { 25 Bare(Vec<T>), 26 Constraint(ValueSequenceConstraint<T>), 27 } 28 29 impl<T> Default for BareOrValueSequenceConstraint<T> { 30 fn default() -> Self { 31 Self::Constraint(Default::default()) 32 } 33 } 34 35 impl<T> From<T> for BareOrValueSequenceConstraint<T> { 36 fn from(bare: T) -> Self { 37 Self::Bare(vec![bare]) 38 } 39 } 40 41 impl<T> From<Vec<T>> for BareOrValueSequenceConstraint<T> { 42 fn from(bare: Vec<T>) -> Self { 43 Self::Bare(bare) 44 } 45 } 46 47 impl<T> From<ValueSequenceConstraint<T>> for BareOrValueSequenceConstraint<T> { 48 fn from(constraint: ValueSequenceConstraint<T>) -> Self { 49 Self::Constraint(constraint) 50 } 51 } 52 53 impl<T> BareOrValueSequenceConstraint<T> 54 where 55 T: Clone, 56 { 57 pub fn to_resolved( 58 &self, 59 strategy: MediaTrackConstraintResolutionStrategy, 60 ) -> ValueSequenceConstraint<T> { 61 self.clone().into_resolved(strategy) 62 } 63 64 pub fn into_resolved( 65 self, 66 strategy: MediaTrackConstraintResolutionStrategy, 67 ) -> ValueSequenceConstraint<T> { 68 match self { 69 Self::Bare(bare) => match strategy { 70 MediaTrackConstraintResolutionStrategy::BareToIdeal => { 71 ValueSequenceConstraint::default().ideal(bare) 72 } 73 MediaTrackConstraintResolutionStrategy::BareToExact => { 74 ValueSequenceConstraint::default().exact(bare) 75 } 76 }, 77 Self::Constraint(constraint) => constraint, 78 } 79 } 80 } 81 82 impl<T> BareOrValueSequenceConstraint<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 `BareOrValueSequenceConstraint<T>` type aims to be a 98 /// generalization over multiple types in the W3C spec: 99 /// 100 /// | Rust | W3C | 101 /// | --------------------------------- | ----------------------------------------------------------------- | 102 /// | `ValueSequenceConstraint<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 ValueSequenceConstraint<T> { 110 // See https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints#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> ValueSequenceConstraint<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 ValueSequenceConstraint<T> { 168 fn default() -> Self { 169 Self { 170 exact: None, 171 ideal: None, 172 } 173 } 174 } 175 176 impl<T> std::fmt::Display for ValueSequenceConstraint<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 resolve_to_advanced() { 208 let constraint = BareOrValueSequenceConstraint::Bare(vec![true]); 209 let strategy = MediaTrackConstraintResolutionStrategy::BareToExact; 210 let actual: ValueSequenceConstraint<bool> = constraint.into_resolved(strategy); 211 let expected = ValueSequenceConstraint::default().exact(vec![true]); 212 213 assert_eq!(actual, expected); 214 } 215 216 #[test] 217 fn resolve_to_basic() { 218 let constraint = BareOrValueSequenceConstraint::Bare(vec![true]); 219 let strategy = MediaTrackConstraintResolutionStrategy::BareToIdeal; 220 let actual: ValueSequenceConstraint<bool> = constraint.into_resolved(strategy); 221 let expected = ValueSequenceConstraint::default().ideal(vec![true]); 222 223 assert_eq!(actual, expected); 224 } 225 } 226 227 #[cfg(feature = "serde")] 228 #[cfg(test)] 229 mod serde_tests { 230 use crate::macros::test_serde_symmetry; 231 232 use super::*; 233 234 macro_rules! test_serde { 235 ($t:ty => { 236 values: [$($values:expr),*] 237 }) => { 238 type Subject = BareOrValueSequenceConstraint<$t>; 239 240 #[test] 241 fn default() { 242 let subject = Subject::default(); 243 let json = serde_json::json!({}); 244 245 test_serde_symmetry!(subject: subject, json: json); 246 } 247 248 #[test] 249 fn bare() { 250 let subject = Subject::Bare(vec![$($values.to_owned()),*].into()); 251 let json = serde_json::json!([$($values),*]); 252 253 test_serde_symmetry!(subject: subject, json: json); 254 } 255 256 #[test] 257 fn exact_constraint() { 258 let subject = Subject::Constraint(ValueSequenceConstraint::default().exact(vec![$($values.to_owned()),*])); 259 let json = serde_json::json!({ 260 "exact": [$($values),*], 261 }); 262 263 test_serde_symmetry!(subject: subject, json: json); 264 } 265 266 #[test] 267 fn ideal_constraint() { 268 let subject = Subject::Constraint(ValueSequenceConstraint::default().ideal(vec![$($values.to_owned()),*])); 269 let json = serde_json::json!({ 270 "ideal": [$($values),*], 271 }); 272 273 test_serde_symmetry!(subject: subject, json: json); 274 } 275 276 #[test] 277 fn full_constraint() { 278 let subject = Subject::Constraint(ValueSequenceConstraint::default().exact(vec![$($values.to_owned()),*]).ideal(vec![$($values.to_owned()),*])); 279 let json = serde_json::json!({ 280 "exact": [$($values),*], 281 "ideal": [$($values),*], 282 }); 283 284 test_serde_symmetry!(subject: subject, json: json); 285 } 286 }; 287 } 288 289 mod string { 290 use super::*; 291 292 test_serde!(String => { 293 values: ["VALUE_0", "VALUE_1"] 294 }); 295 } 296 } 297