webrtc_constraints/constraint/
value.rs1#[cfg(feature = "serde")]
2use serde::{Deserialize, Serialize};
3
4use crate::MediaTrackConstraintResolutionStrategy;
5
6#[derive(Debug, Clone, Eq, PartialEq)]
22#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
23#[cfg_attr(feature = "serde", serde(untagged))]
24pub enum ValueConstraint<T> {
25 Bare(T),
27 Constraint(ResolvedValueConstraint<T>),
29}
30
31impl<T> Default for ValueConstraint<T> {
32 fn default() -> Self {
33 Self::Constraint(Default::default())
34 }
35}
36
37impl<T> From<T> for ValueConstraint<T> {
38 fn from(bare: T) -> Self {
39 Self::Bare(bare)
40 }
41}
42
43impl<T> From<ResolvedValueConstraint<T>> for ValueConstraint<T> {
44 fn from(constraint: ResolvedValueConstraint<T>) -> Self {
45 Self::Constraint(constraint)
46 }
47}
48
49impl<T> ValueConstraint<T>
50where
51 T: Clone,
52{
53 pub fn to_resolved(
56 &self,
57 strategy: MediaTrackConstraintResolutionStrategy,
58 ) -> ResolvedValueConstraint<T> {
59 self.clone().into_resolved(strategy)
60 }
61
62 pub fn into_resolved(
65 self,
66 strategy: MediaTrackConstraintResolutionStrategy,
67 ) -> ResolvedValueConstraint<T> {
68 match self {
69 Self::Bare(bare) => match strategy {
70 MediaTrackConstraintResolutionStrategy::BareToIdeal => {
71 ResolvedValueConstraint::default().ideal(bare)
72 }
73 MediaTrackConstraintResolutionStrategy::BareToExact => {
74 ResolvedValueConstraint::default().exact(bare)
75 }
76 },
77 Self::Constraint(constraint) => constraint,
78 }
79 }
80}
81
82impl<T> ValueConstraint<T> {
83 pub fn is_empty(&self) -> bool {
85 match self {
86 Self::Bare(_) => false,
87 Self::Constraint(constraint) => constraint.is_empty(),
88 }
89 }
90}
91
92#[derive(Debug, Clone, Eq, PartialEq)]
108#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
109#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
110pub struct ResolvedValueConstraint<T> {
111 #[cfg_attr(
115 feature = "serde",
116 serde(skip_serializing_if = "core::option::Option::is_none")
117 )]
118 pub exact: Option<T>,
119 #[cfg_attr(
123 feature = "serde",
124 serde(skip_serializing_if = "core::option::Option::is_none")
125 )]
126 pub ideal: Option<T>,
127}
128
129impl<T> ResolvedValueConstraint<T> {
130 #[inline]
133 pub fn exact<U>(mut self, exact: U) -> Self
134 where
135 Option<T>: From<U>,
136 {
137 self.exact = exact.into();
138 self
139 }
140
141 #[inline]
144 pub fn ideal<U>(mut self, ideal: U) -> Self
145 where
146 Option<T>: From<U>,
147 {
148 self.ideal = ideal.into();
149 self
150 }
151
152 pub fn is_required(&self) -> bool {
155 self.exact.is_some()
156 }
157
158 pub fn is_empty(&self) -> bool {
161 self.exact.is_none() && self.ideal.is_none()
162 }
163
164 pub fn to_required_only(&self) -> Self
166 where
167 T: Clone,
168 {
169 self.clone().into_required_only()
170 }
171
172 pub fn into_required_only(self) -> Self {
175 Self {
176 exact: self.exact,
177 ideal: None,
178 }
179 }
180}
181
182impl<T> Default for ResolvedValueConstraint<T> {
183 #[inline]
184 fn default() -> Self {
185 Self {
186 exact: None,
187 ideal: None,
188 }
189 }
190}
191
192impl<T> std::fmt::Display for ResolvedValueConstraint<T>
193where
194 T: std::fmt::Debug,
195{
196 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
197 let mut is_first = true;
198 f.write_str("(")?;
199 if let Some(ref exact) = &self.exact {
200 f.write_fmt(format_args!("x == {:?}", exact))?;
201 is_first = false;
202 }
203 if let Some(ref ideal) = &self.ideal {
204 if !is_first {
205 f.write_str(" && ")?;
206 }
207 f.write_fmt(format_args!("x ~= {:?}", ideal))?;
208 is_first = false;
209 }
210 if is_first {
211 f.write_str("<empty>")?;
212 }
213 f.write_str(")")?;
214 Ok(())
215 }
216}
217
218#[cfg(test)]
219mod tests {
220 use super::*;
221
222 #[test]
223 fn to_string() {
224 let scenarios = [
225 (ResolvedValueConstraint::default(), "(<empty>)"),
226 (
227 ResolvedValueConstraint::default().exact(true),
228 "(x == true)",
229 ),
230 (
231 ResolvedValueConstraint::default().ideal(true),
232 "(x ~= true)",
233 ),
234 (
235 ResolvedValueConstraint::default().exact(true).ideal(true),
236 "(x == true && x ~= true)",
237 ),
238 ];
239
240 for (constraint, expected) in scenarios {
241 let actual = constraint.to_string();
242
243 assert_eq!(actual, expected);
244 }
245 }
246
247 #[test]
248 fn is_required() {
249 let scenarios = [
250 (ResolvedValueConstraint::default(), false),
251 (ResolvedValueConstraint::default().exact(true), true),
252 (ResolvedValueConstraint::default().ideal(true), false),
253 (
254 ResolvedValueConstraint::default().exact(true).ideal(true),
255 true,
256 ),
257 ];
258
259 for (constraint, expected) in scenarios {
260 let actual = constraint.is_required();
261
262 assert_eq!(actual, expected);
263 }
264 }
265
266 mod is_empty {
267 use super::*;
268
269 #[test]
270 fn bare() {
271 let constraint = ValueConstraint::Bare(true);
272
273 assert!(!constraint.is_empty());
274 }
275
276 #[test]
277 fn constraint() {
278 let scenarios = [
279 (ResolvedValueConstraint::default(), true),
280 (ResolvedValueConstraint::default().exact(true), false),
281 (ResolvedValueConstraint::default().ideal(true), false),
282 (
283 ResolvedValueConstraint::default().exact(true).ideal(true),
284 false,
285 ),
286 ];
287
288 for (constraint, expected) in scenarios {
289 let constraint = ValueConstraint::<bool>::Constraint(constraint);
290
291 let actual = constraint.is_empty();
292
293 assert_eq!(actual, expected);
294 }
295 }
296 }
297
298 #[test]
299 fn resolve_to_advanced() {
300 let constraints = [
301 ValueConstraint::Bare(true),
302 ValueConstraint::Constraint(ResolvedValueConstraint::default().exact(true)),
303 ];
304 let strategy = MediaTrackConstraintResolutionStrategy::BareToExact;
305
306 for constraint in constraints {
307 let actuals = [
308 constraint.to_resolved(strategy),
309 constraint.into_resolved(strategy),
310 ];
311
312 let expected = ResolvedValueConstraint::default().exact(true);
313
314 for actual in actuals {
315 assert_eq!(actual, expected);
316 }
317 }
318 }
319
320 #[test]
321 fn resolve_to_basic() {
322 let constraints = [
323 ValueConstraint::Bare(true),
324 ValueConstraint::Constraint(ResolvedValueConstraint::default().ideal(true)),
325 ];
326 let strategy = MediaTrackConstraintResolutionStrategy::BareToIdeal;
327
328 for constraint in constraints {
329 let actuals = [
330 constraint.to_resolved(strategy),
331 constraint.into_resolved(strategy),
332 ];
333
334 let expected = ResolvedValueConstraint::default().ideal(true);
335
336 for actual in actuals {
337 assert_eq!(actual, expected);
338 }
339 }
340 }
341}
342
343#[cfg(feature = "serde")]
344#[cfg(test)]
345mod serde_tests {
346 use crate::macros::test_serde_symmetry;
347
348 use super::*;
349
350 macro_rules! test_serde {
351 ($t:ty => {
352 value: $value:expr
353 }) => {
354 type Subject = ValueConstraint<$t>;
355
356 #[test]
357 fn default() {
358 let subject = Subject::default();
359 let json = serde_json::json!({});
360
361 test_serde_symmetry!(subject: subject, json: json);
362 }
363
364 #[test]
365 fn bare() {
366 let subject = Subject::Bare($value.to_owned());
367 let json = serde_json::json!($value);
368
369 test_serde_symmetry!(subject: subject, json: json);
370 }
371
372 #[test]
373 fn exact_constraint() {
374 let subject = Subject::Constraint(ResolvedValueConstraint::default().exact($value.to_owned()));
375 let json = serde_json::json!({
376 "exact": $value,
377 });
378
379 test_serde_symmetry!(subject: subject, json: json);
380 }
381
382 #[test]
383 fn ideal_constraint() {
384 let subject = Subject::Constraint(ResolvedValueConstraint::default().ideal($value.to_owned()));
385 let json = serde_json::json!({
386 "ideal": $value,
387 });
388
389 test_serde_symmetry!(subject: subject, json: json);
390 }
391
392 #[test]
393 fn full_constraint() {
394 let subject = Subject::Constraint(ResolvedValueConstraint::default().exact($value.to_owned()).ideal($value.to_owned()));
395 let json = serde_json::json!({
396 "exact": $value,
397 "ideal": $value,
398 });
399
400 test_serde_symmetry!(subject: subject, json: json);
401 }
402 };
403 }
404
405 mod bool {
406 use super::*;
407
408 test_serde!(bool => {
409 value: true
410 });
411 }
412
413 mod string {
414 use super::*;
415
416 test_serde!(String => {
417 value: "VALUE"
418 });
419 }
420}