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, 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::ideal_only(bare) 66 } 67 MediaTrackConstraintResolutionStrategy::BareToExact => { 68 ValueConstraint::exact_only(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, 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 pub fn exact_only(exact: T) -> Self { 118 Self { 119 exact: Some(exact), 120 ideal: None, 121 } 122 } 123 124 pub fn ideal_only(ideal: T) -> Self { 125 Self { 126 exact: None, 127 ideal: Some(ideal), 128 } 129 } 130 131 pub fn is_required(&self) -> bool { 132 self.exact.is_some() 133 } 134 135 pub fn is_empty(&self) -> bool { 136 self.exact.is_none() && self.ideal.is_none() 137 } 138 } 139 140 impl<T> Default for ValueConstraint<T> { 141 fn default() -> Self { 142 Self { 143 exact: None, 144 ideal: None, 145 } 146 } 147 } 148 149 #[cfg(test)] 150 mod tests { 151 use super::*; 152 153 #[test] 154 fn resolve_to_advanced() { 155 let constraint = BareOrValueConstraint::Bare(true); 156 let strategy = MediaTrackConstraintResolutionStrategy::BareToExact; 157 let actual: ValueConstraint<bool> = constraint.into_resolved(strategy); 158 let expected = ValueConstraint::exact_only(true); 159 160 assert_eq!(actual, expected); 161 } 162 163 #[test] 164 fn resolve_to_basic() { 165 let constraint = BareOrValueConstraint::Bare(true); 166 let strategy = MediaTrackConstraintResolutionStrategy::BareToIdeal; 167 let actual: ValueConstraint<bool> = constraint.into_resolved(strategy); 168 let expected = ValueConstraint::ideal_only(true); 169 170 assert_eq!(actual, expected); 171 } 172 } 173 174 #[cfg(feature = "serde")] 175 #[cfg(test)] 176 mod serde_tests { 177 use crate::macros::test_serde_symmetry; 178 179 use super::*; 180 181 macro_rules! test_serde { 182 ($t:ty => { 183 value: $value:expr 184 }) => { 185 type Subject = BareOrValueConstraint<$t>; 186 187 #[test] 188 fn default() { 189 let subject = Subject::default(); 190 let json = serde_json::json!({}); 191 192 test_serde_symmetry!(subject: subject, json: json); 193 } 194 195 #[test] 196 fn bare() { 197 let subject = Subject::Bare($value.to_owned()); 198 let json = serde_json::json!($value); 199 200 test_serde_symmetry!(subject: subject, json: json); 201 } 202 203 #[test] 204 fn exact() { 205 let subject = Subject::Constraint(ValueConstraint::exact_only($value.to_owned())); 206 let json = serde_json::json!({ 207 "exact": $value, 208 }); 209 210 test_serde_symmetry!(subject: subject, json: json); 211 } 212 213 #[test] 214 fn ideal() { 215 let subject = Subject::Constraint(ValueConstraint::ideal_only($value.to_owned())); 216 let json = serde_json::json!({ 217 "ideal": $value, 218 }); 219 220 test_serde_symmetry!(subject: subject, json: json); 221 } 222 }; 223 } 224 225 mod bool { 226 use super::*; 227 228 test_serde!(bool => { 229 value: true 230 }); 231 } 232 233 mod string { 234 use super::*; 235 236 test_serde!(String => { 237 value: "VALUE" 238 }); 239 } 240 } 241