webrtc_constraints/
supported_constraints.rs

1use std::{
2    collections::HashSet,
3    iter::FromIterator,
4    ops::{Deref, DerefMut},
5};
6
7#[cfg(feature = "serde")]
8use serde::{
9    de::{MapAccess, Visitor},
10    ser::SerializeMap,
11    Deserialize, Deserializer, Serialize, Serializer,
12};
13
14use 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)]
36pub struct MediaTrackSupportedConstraints(HashSet<MediaTrackProperty>);
37
38impl MediaTrackSupportedConstraints {
39    /// Creates a supported constraints value from its inner hashmap.
40    pub fn new(properties: HashSet<MediaTrackProperty>) -> Self {
41        Self(properties)
42    }
43
44    /// Consumes the value, returning its inner hashmap.
45    pub fn into_inner(self) -> HashSet<MediaTrackProperty> {
46        self.0
47    }
48}
49
50impl Deref for MediaTrackSupportedConstraints {
51    type Target = HashSet<MediaTrackProperty>;
52
53    fn deref(&self) -> &Self::Target {
54        &self.0
55    }
56}
57
58impl DerefMut for MediaTrackSupportedConstraints {
59    fn deref_mut(&mut self) -> &mut Self::Target {
60        &mut self.0
61    }
62}
63
64impl 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
68    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
75impl<T> FromIterator<T> for MediaTrackSupportedConstraints
76where
77    T: Into<MediaTrackProperty>,
78{
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
87impl IntoIterator for MediaTrackSupportedConstraints {
88    type Item = MediaTrackProperty;
89    type IntoIter = std::collections::hash_set::IntoIter<MediaTrackProperty>;
90
91    fn into_iter(self) -> Self::IntoIter {
92        self.0.into_iter()
93    }
94}
95
96#[cfg(feature = "serde")]
97impl<'de> Deserialize<'de> for MediaTrackSupportedConstraints {
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")]
107impl Serialize for MediaTrackSupportedConstraints {
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")]
121struct SerdeVisitor;
122
123#[cfg(feature = "serde")]
124impl<'de> Visitor<'de> for SerdeVisitor {
125    type Value = MediaTrackSupportedConstraints;
126
127    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
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)]
146mod tests {
147    use crate::property::all::name::*;
148
149    use super::*;
150
151    type Subject = MediaTrackSupportedConstraints;
152
153    #[test]
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]
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]
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)]
203mod serde_tests {
204    use crate::{macros::test_serde_symmetry, property::all::name::*};
205
206    use super::*;
207
208    type Subject = MediaTrackSupportedConstraints;
209
210    #[test]
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]
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}