1 #[cfg(feature = "serde")] 2 use serde::{Deserialize, Serialize}; 3 4 use crate::MediaTrackConstraintResolutionStrategy; 5 6 /// A bare value or constraint specifying a single accepted value. 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 /// | `BareOrValueConstraint<bool>` | [`ConstrainBoolean`][constrain_boolean] | 18 /// 19 /// [constrain_boolean]: https://www.w3.org/TR/mediacapture-streams/#dom-constrainboolean 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 BareOrValueConstraint<T> { 25 Bare(T), 26 Constraint(ValueConstraint<T>), 27 } 28 29 impl<T> Default for BareOrValueConstraint<T> { 30 fn default() -> Self { 31 Self::Constraint(Default::default()) 32 } 33 } 34 35 impl<T> From<T> for BareOrValueConstraint<T> { 36 fn from(bare: T) -> Self { 37 Self::Bare(bare) 38 } 39 } 40 41 impl<T> From<ValueConstraint<T>> for BareOrValueConstraint<T> { 42 fn from(constraint: ValueConstraint<T>) -> Self { 43 Self::Constraint(constraint) 44 } 45 } 46 47 impl<T> BareOrValueConstraint<T> 48 where 49 T: Clone, 50 { 51 pub fn to_resolved( 52 &self, 53 strategy: MediaTrackConstraintResolutionStrategy, 54 ) -> ValueConstraint<T> { 55 self.clone().into_resolved(strategy) 56 } 57 58 pub fn into_resolved( 59 self, 60 strategy: MediaTrackConstraintResolutionStrategy, 61 ) -> ValueConstraint<T> { 62 match self { 63 Self::Bare(bare) => match strategy { 64 MediaTrackConstraintResolutionStrategy::BareToIdeal => { 65 ValueConstraint::default().ideal(bare) 66 } 67 MediaTrackConstraintResolutionStrategy::BareToExact => { 68 ValueConstraint::default().exact(bare) 69 } 70 }, 71 Self::Constraint(constraint) => constraint, 72 } 73 } 74 } 75 76 impl<T> BareOrValueConstraint<T> { 77 pub fn is_empty(&self) -> bool { 78 match self { 79 Self::Bare(_) => false, 80 Self::Constraint(constraint) => constraint.is_empty(), 81 } 82 } 83 } 84 85 /// A constraint specifying a single accepted value. 86 /// 87 /// # W3C Spec Compliance 88 /// 89 /// There exists no direct corresponding type in the 90 /// W3C ["Media Capture and Streams"][media_capture_and_streams_spec] spec, 91 /// since the `BareOrValueConstraint<T>` type aims to be a 92 /// generalization over multiple types in the W3C spec: 93 /// 94 /// | Rust | W3C | 95 /// | ------------------------------ | --------------------------------------- | 96 /// | `ValueConstraint<bool>` | [`ConstrainBooleanParameters`][constrain_boolean_parameters] | 97 /// 98 /// [constrain_boolean_parameters]: https://www.w3.org/TR/mediacapture-streams/#dom-constrainbooleanparameters 99 /// [media_capture_and_streams_spec]: https://www.w3.org/TR/mediacapture-streams/ 100 #[derive(Debug, Clone, Eq, PartialEq)] 101 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 102 #[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] 103 pub struct ValueConstraint<T> { 104 #[cfg_attr( 105 feature = "serde", 106 serde(skip_serializing_if = "core::option::Option::is_none") 107 )] 108 pub exact: Option<T>, 109 #[cfg_attr( 110 feature = "serde", 111 serde(skip_serializing_if = "core::option::Option::is_none") 112 )] 113 pub ideal: Option<T>, 114 } 115 116 impl<T> ValueConstraint<T> { 117 #[inline] 118 pub fn exact<U>(mut self, exact: U) -> Self 119 where 120 Option<T>: From<U>, 121 { 122 self.exact = exact.into(); 123 self 124 } 125 126 #[inline] 127 pub fn ideal<U>(mut self, ideal: U) -> Self 128 where 129 Option<T>: From<U>, 130 { 131 self.ideal = ideal.into(); 132 self 133 } 134 135 pub fn is_required(&self) -> bool { 136 self.exact.is_some() 137 } 138 139 pub fn is_empty(&self) -> bool { 140 self.exact.is_none() && self.ideal.is_none() 141 } 142 143 pub fn to_required_only(&self) -> Self 144 where 145 T: Clone, 146 { 147 self.clone().into_required_only() 148 } 149 150 pub fn into_required_only(self) -> Self { 151 Self { 152 exact: self.exact, 153 ideal: None, 154 } 155 } 156 } 157 158 impl<T> Default for ValueConstraint<T> { 159 #[inline] 160 fn default() -> Self { 161 Self { 162 exact: None, 163 ideal: None, 164 } 165 } 166 } 167 168 impl<T> std::fmt::Display for ValueConstraint<T> 169 where 170 T: std::fmt::Debug, 171 { 172 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 173 let mut is_first = true; 174 f.write_str("(")?; 175 if let Some(ref exact) = &self.exact { 176 f.write_fmt(format_args!("x == {:?}", exact))?; 177 is_first = false; 178 } 179 if let Some(ref ideal) = &self.ideal { 180 if !is_first { 181 f.write_str(" && ")?; 182 } 183 f.write_fmt(format_args!("x ~= {:?}", ideal))?; 184 is_first = false; 185 } 186 if is_first { 187 f.write_str("<empty>")?; 188 } 189 f.write_str(")")?; 190 Ok(()) 191 } 192 } 193 194 #[cfg(test)] 195 mod tests { 196 use super::*; 197 198 #[test] 199 fn resolve_to_advanced() { 200 let constraint = BareOrValueConstraint::Bare(true); 201 let strategy = MediaTrackConstraintResolutionStrategy::BareToExact; 202 let actual: ValueConstraint<bool> = constraint.into_resolved(strategy); 203 let expected = ValueConstraint::default().exact(true); 204 205 assert_eq!(actual, expected); 206 } 207 208 #[test] 209 fn resolve_to_basic() { 210 let constraint = BareOrValueConstraint::Bare(true); 211 let strategy = MediaTrackConstraintResolutionStrategy::BareToIdeal; 212 let actual: ValueConstraint<bool> = constraint.into_resolved(strategy); 213 let expected = ValueConstraint::default().ideal(true); 214 215 assert_eq!(actual, expected); 216 } 217 } 218 219 #[cfg(feature = "serde")] 220 #[cfg(test)] 221 mod serde_tests { 222 use crate::macros::test_serde_symmetry; 223 224 use super::*; 225 226 macro_rules! test_serde { 227 ($t:ty => { 228 value: $value:expr 229 }) => { 230 type Subject = BareOrValueConstraint<$t>; 231 232 #[test] 233 fn default() { 234 let subject = Subject::default(); 235 let json = serde_json::json!({}); 236 237 test_serde_symmetry!(subject: subject, json: json); 238 } 239 240 #[test] 241 fn bare() { 242 let subject = Subject::Bare($value.to_owned()); 243 let json = serde_json::json!($value); 244 245 test_serde_symmetry!(subject: subject, json: json); 246 } 247 248 #[test] 249 fn exact_constraint() { 250 let subject = Subject::Constraint(ValueConstraint::default().exact($value.to_owned())); 251 let json = serde_json::json!({ 252 "exact": $value, 253 }); 254 255 test_serde_symmetry!(subject: subject, json: json); 256 } 257 258 #[test] 259 fn ideal_constraint() { 260 let subject = Subject::Constraint(ValueConstraint::default().ideal($value.to_owned())); 261 let json = serde_json::json!({ 262 "ideal": $value, 263 }); 264 265 test_serde_symmetry!(subject: subject, json: json); 266 } 267 268 #[test] 269 fn full_constraint() { 270 let subject = Subject::Constraint(ValueConstraint::default().exact($value.to_owned()).ideal($value.to_owned())); 271 let json = serde_json::json!({ 272 "exact": $value, 273 "ideal": $value, 274 }); 275 276 test_serde_symmetry!(subject: subject, json: json); 277 } 278 }; 279 } 280 281 mod bool { 282 use super::*; 283 284 test_serde!(bool => { 285 value: true 286 }); 287 } 288 289 mod string { 290 use super::*; 291 292 test_serde!(String => { 293 value: "VALUE" 294 }); 295 } 296 } 297