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