1 use std::{ 2 collections::HashSet, 3 iter::FromIterator, 4 ops::{Deref, DerefMut}, 5 }; 6 7 #[cfg(feature = "serde")] 8 use serde::{ 9 de::{MapAccess, Visitor}, 10 ser::SerializeMap, 11 Deserialize, Deserializer, Serialize, Serializer, 12 }; 13 14 use crate::MediaTrackProperty; 15 16 /// The list of constraints recognized by a User Agent for controlling the 17 /// capabilities of a [`MediaStreamTrack`][media_stream_track] object. 18 /// 19 /// # W3C Spec Compliance 20 /// 21 /// Corresponds to [`MediaTrackSupportedConstraints`][media_track_supported_constraints] 22 /// from the W3C ["Media Capture and Streams"][media_capture_and_streams_spec] spec. 23 /// 24 /// The W3C spec defines `MediaTrackSupportedConstraints` in terma of a dictionary, 25 /// which per the [WebIDL spec][webidl_spec] is an ordered map (e.g. [`IndexSet<K>`][index_set]). 26 /// Since the spec however does not make use of the order of items 27 /// in the map we use a simple `HashSet<K>`. 28 /// 29 /// [hash_set]: https://doc.rust-lang.org/std/collections/struct.HashSet.html 30 /// [index_set]: https://docs.rs/indexmap/latest/indexmap/set/struct.IndexSet.html 31 /// [media_stream_track]: https://www.w3.org/TR/mediacapture-streams/#dom-mediastreamtrack 32 /// [media_track_supported_constraints]: https://www.w3.org/TR/mediacapture-streams/#dom-mediatracksupportedconstraints 33 /// [media_capture_and_streams_spec]: https://www.w3.org/TR/mediacapture-streams 34 /// [webidl_spec]: https://webidl.spec.whatwg.org/#idl-dictionaries 35 #[derive(Debug, Clone, Eq, PartialEq)] 36 pub struct MediaTrackSupportedConstraints(HashSet<MediaTrackProperty>); 37 38 impl MediaTrackSupportedConstraints { 39 /// Creates a supported constraints value from its inner hashmap. new(properties: HashSet<MediaTrackProperty>) -> Self40 pub fn new(properties: HashSet<MediaTrackProperty>) -> Self { 41 Self(properties) 42 } 43 44 /// Consumes the value, returning its inner hashmap. into_inner(self) -> HashSet<MediaTrackProperty>45 pub fn into_inner(self) -> HashSet<MediaTrackProperty> { 46 self.0 47 } 48 } 49 50 impl Deref for MediaTrackSupportedConstraints { 51 type Target = HashSet<MediaTrackProperty>; 52 deref(&self) -> &Self::Target53 fn deref(&self) -> &Self::Target { 54 &self.0 55 } 56 } 57 58 impl DerefMut for MediaTrackSupportedConstraints { deref_mut(&mut self) -> &mut Self::Target59 fn deref_mut(&mut self) -> &mut Self::Target { 60 &mut self.0 61 } 62 } 63 64 impl Default for MediaTrackSupportedConstraints { 65 /// [Default values][default_values] as defined by the W3C specification. 66 /// 67 /// [default_values]: https://www.w3.org/TR/mediacapture-streams/#dictionary-mediatracksupportedconstraints-members default() -> Self68 fn default() -> Self { 69 use crate::property::all::names as property_names; 70 71 Self::from_iter(property_names().into_iter().cloned()) 72 } 73 } 74 75 impl<T> FromIterator<T> for MediaTrackSupportedConstraints 76 where 77 T: Into<MediaTrackProperty>, 78 { from_iter<I>(iter: I) -> Self where I: IntoIterator<Item = T>,79 fn from_iter<I>(iter: I) -> Self 80 where 81 I: IntoIterator<Item = T>, 82 { 83 Self(iter.into_iter().map(|property| property.into()).collect()) 84 } 85 } 86 87 impl IntoIterator for MediaTrackSupportedConstraints { 88 type Item = MediaTrackProperty; 89 type IntoIter = std::collections::hash_set::IntoIter<MediaTrackProperty>; 90 into_iter(self) -> Self::IntoIter91 fn into_iter(self) -> Self::IntoIter { 92 self.0.into_iter() 93 } 94 } 95 96 #[cfg(feature = "serde")] 97 impl<'de> Deserialize<'de> for MediaTrackSupportedConstraints { deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de>,98 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 99 where 100 D: Deserializer<'de>, 101 { 102 deserializer.deserialize_map(SerdeVisitor) 103 } 104 } 105 106 #[cfg(feature = "serde")] 107 impl Serialize for MediaTrackSupportedConstraints { serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer,108 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 109 where 110 S: Serializer, 111 { 112 let mut map = serializer.serialize_map(Some(self.0.len()))?; 113 for property in &self.0 { 114 map.serialize_entry(property, &true)?; 115 } 116 map.end() 117 } 118 } 119 120 #[cfg(feature = "serde")] 121 struct SerdeVisitor; 122 123 #[cfg(feature = "serde")] 124 impl<'de> Visitor<'de> for SerdeVisitor { 125 type Value = MediaTrackSupportedConstraints; 126 expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result127 fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 128 formatter.write_str("an object with strings as keys and `true` as values") 129 } 130 visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error> where M: MapAccess<'de>,131 fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error> 132 where 133 M: MapAccess<'de>, 134 { 135 let mut set = HashSet::with_capacity(access.size_hint().unwrap_or(0)); 136 while let Some((key, value)) = access.next_entry()? { 137 if value { 138 set.insert(key); 139 } 140 } 141 Ok(MediaTrackSupportedConstraints(set)) 142 } 143 } 144 145 #[cfg(test)] 146 mod tests { 147 use crate::property::all::name::*; 148 149 use super::*; 150 151 type Subject = MediaTrackSupportedConstraints; 152 153 #[test] into_inner()154 fn into_inner() { 155 let hash_set = HashSet::from_iter([ 156 DEVICE_ID.clone(), 157 AUTO_GAIN_CONTROL.clone(), 158 CHANNEL_COUNT.clone(), 159 LATENCY.clone(), 160 ]); 161 162 let subject = Subject::new(hash_set.clone()); 163 164 let actual = subject.into_inner(); 165 166 let expected = hash_set; 167 168 assert_eq!(actual, expected); 169 } 170 171 #[test] into_iter()172 fn into_iter() { 173 let hash_set = HashSet::from_iter([ 174 DEVICE_ID.clone(), 175 AUTO_GAIN_CONTROL.clone(), 176 CHANNEL_COUNT.clone(), 177 LATENCY.clone(), 178 ]); 179 180 let subject = Subject::new(hash_set.clone()); 181 182 let actual: HashSet<_, _> = subject.into_iter().collect(); 183 184 let expected = hash_set; 185 186 assert_eq!(actual, expected); 187 } 188 189 #[test] deref_and_deref_mut()190 fn deref_and_deref_mut() { 191 let mut subject = Subject::default(); 192 193 // Deref mut: 194 subject.insert(DEVICE_ID.clone()); 195 196 // Deref: 197 assert!(subject.contains(&DEVICE_ID)); 198 } 199 } 200 201 #[cfg(feature = "serde")] 202 #[cfg(test)] 203 mod serde_tests { 204 use crate::{macros::test_serde_symmetry, property::all::name::*}; 205 206 use super::*; 207 208 type Subject = MediaTrackSupportedConstraints; 209 210 #[test] default()211 fn default() { 212 let subject = Subject::default(); 213 let json = serde_json::json!({ 214 "deviceId": true, 215 "groupId": true, 216 "autoGainControl": true, 217 "channelCount": true, 218 "echoCancellation": true, 219 "latency": true, 220 "noiseSuppression": true, 221 "sampleRate": true, 222 "sampleSize": true, 223 "aspectRatio": true, 224 "facingMode": true, 225 "frameRate": true, 226 "height": true, 227 "width": true, 228 "resizeMode": true, 229 }); 230 231 test_serde_symmetry!(subject: subject, json: json); 232 } 233 234 #[test] customized()235 fn customized() { 236 let subject = Subject::from_iter([ 237 &DEVICE_ID, 238 &GROUP_ID, 239 &AUTO_GAIN_CONTROL, 240 &CHANNEL_COUNT, 241 &ASPECT_RATIO, 242 &FACING_MODE, 243 ]); 244 let json = serde_json::json!({ 245 "deviceId": true, 246 "groupId": true, 247 "autoGainControl": true, 248 "channelCount": true, 249 "aspectRatio": true, 250 "facingMode": true 251 }); 252 253 test_serde_symmetry!(subject: subject, json: json); 254 } 255 } 256