webrtc_constraints/algorithms/fitness_distance/value_sequence_constraint.rs
1use crate::ResolvedValueSequenceConstraint;
2
3use super::{
4 setting::SettingFitnessDistanceError, FitnessDistance, SettingFitnessDistanceErrorKind,
5};
6
7macro_rules! impl_non_numeric_value_sequence_constraint {
8 (setting: $s:ty, constraint: $c:ty) => {
9 impl<'a> FitnessDistance<Option<&'a $s>> for ResolvedValueSequenceConstraint<$c>
10 where
11 $s: PartialEq<$c>,
12 {
13 type Error = SettingFitnessDistanceError;
14
15 fn fitness_distance(&self, setting: Option<&'a $s>) -> Result<f64, Self::Error> {
16 if let Some(exact) = self.exact.as_ref() {
17 // As specified in step 2 of the `fitness distance` algorithm:
18 // <https://www.w3.org/TR/mediacapture-streams/#dfn-fitness-distance>
19 //
20 // > If the constraint is required (constraintValue either contains
21 // > one or more members named […] 'exact' […]), and the settings
22 // > dictionary's constraintName member's value does not satisfy the
23 // > constraint or doesn't exist, the fitness distance is positive infinity.
24 match setting {
25 Some(actual) if exact.contains(actual) => {}
26 Some(setting) => {
27 return Err(SettingFitnessDistanceError {
28 kind: SettingFitnessDistanceErrorKind::Mismatch,
29 constraint: format!("{}", self.to_required_only()),
30 setting: Some(format!("{:?}", setting)),
31 })
32 }
33 None => {
34 return Err(SettingFitnessDistanceError {
35 kind: SettingFitnessDistanceErrorKind::Missing,
36 constraint: format!("{}", self.to_required_only()),
37 setting: None,
38 })
39 }
40 };
41 }
42
43 if let Some(ideal) = self.ideal.as_ref() {
44 // As specified in step 8 of the `fitness distance` algorithm:
45 // <https://www.w3.org/TR/mediacapture-streams/#dfn-fitness-distance>
46 //
47 // > For all string, enum and boolean constraints […],
48 // > the fitness distance is the result of the formula:
49 // >
50 // > ```
51 // > (actual == ideal) ? 0 : 1
52 // > ```
53 //
54 // As well as step 5 of the `fitness distance` algorithm:
55 // <https://www.w3.org/TR/mediacapture-streams/#dfn-fitness-distance>
56 //
57 // > If the settings dictionary's `constraintName` member
58 // > does not exist, the fitness distance is 1.
59 match setting {
60 Some(actual) if ideal.contains(actual) => Ok(0.0),
61 Some(_) => Ok(1.0),
62 None => Ok(1.0),
63 }
64 } else {
65 // As specified in step 6 of the `fitness distance` algorithm:
66 // <https://www.w3.org/TR/mediacapture-streams/#dfn-fitness-distance>
67 //
68 // > If no ideal value is specified (constraintValue either
69 // > contains no member named 'ideal', or, if bare values are to be
70 // > treated as 'ideal', isn't a bare value), the fitness distance is 0.
71 Ok(0.0)
72 }
73 }
74 }
75 };
76}
77
78impl_non_numeric_value_sequence_constraint!(setting: bool, constraint: bool);
79impl_non_numeric_value_sequence_constraint!(setting: String, constraint: String);
80
81macro_rules! impl_numeric_value_sequence_constraint {
82 (setting: $s:ty, constraint: $c:ty) => {
83 impl<'a> FitnessDistance<Option<&'a $s>> for ResolvedValueSequenceConstraint<$c> {
84 type Error = SettingFitnessDistanceError;
85
86 fn fitness_distance(&self, setting: Option<&'a $s>) -> Result<f64, Self::Error> {
87 if let Some(exact) = &self.exact {
88 // As specified in step 2 of the `fitness distance` algorithm:
89 // <https://www.w3.org/TR/mediacapture-streams/#dfn-fitness-distance>
90 //
91 // > If the constraint is required (constraintValue either contains
92 // > one or more members named […] 'exact' […]), and the settings
93 // > dictionary's constraintName member's value does not satisfy the
94 // > constraint or doesn't exist, the fitness distance is positive infinity.
95 match setting {
96 Some(&actual) if exact.contains(&(actual as $c)) => {}
97 Some(setting) => {
98 return Err(SettingFitnessDistanceError {
99 kind: SettingFitnessDistanceErrorKind::Mismatch,
100 constraint: format!("{}", self.to_required_only()),
101 setting: Some(format!("{:?}", setting)),
102 })
103 }
104 None => {
105 return Err(SettingFitnessDistanceError {
106 kind: SettingFitnessDistanceErrorKind::Missing,
107 constraint: format!("{}", self.to_required_only()),
108 setting: None,
109 })
110 }
111 };
112 }
113
114 if let Some(ideal) = &self.ideal {
115 // As specified in step 8 of the `fitness distance` algorithm:
116 // <https://www.w3.org/TR/mediacapture-streams/#dfn-fitness-distance>
117 //
118 // > For all string, enum and boolean constraints […],
119 // > the fitness distance is the result of the formula:
120 // >
121 // > ```
122 // > (actual == ideal) ? 0 : 1
123 // > ```
124 //
125 // As well as step 5 of the `fitness distance` algorithm:
126 // <https://www.w3.org/TR/mediacapture-streams/#dfn-fitness-distance>
127 //
128 // > If the settings dictionary's `constraintName` member
129 // > does not exist, the fitness distance is 1.
130 match setting {
131 Some(&actual) => {
132 let actual: f64 = actual as f64;
133 let mut min_fitness_distance = 1.0;
134 for ideal in ideal.into_iter() {
135 let ideal: f64 = (*ideal) as f64;
136 // As specified in step 7 of the `fitness distance` algorithm:
137 // <https://www.w3.org/TR/mediacapture-streams/#dfn-fitness-distance>
138 //
139 // > For all positive numeric constraints […],
140 // > the fitness distance is the result of the formula
141 // >
142 // > ```
143 // > (actual == ideal) ? 0 : |actual - ideal| / max(|actual|, |ideal|)
144 // > ```
145 let fitness_distance =
146 super::relative_fitness_distance(actual, ideal);
147 if fitness_distance < min_fitness_distance {
148 min_fitness_distance = fitness_distance;
149 }
150 }
151 Ok(min_fitness_distance)
152 }
153 None => Ok(1.0),
154 }
155 } else {
156 // As specified in step 6 of the `fitness distance` algorithm:
157 // <https://www.w3.org/TR/mediacapture-streams/#dfn-fitness-distance>
158 //
159 // > If no ideal value is specified (constraintValue either
160 // > contains no member named 'ideal', or, if bare values are to be
161 // > treated as 'ideal', isn't a bare value), the fitness distance is 0.
162 Ok(0.0)
163 }
164 }
165 }
166 };
167}
168
169impl_numeric_value_sequence_constraint!(setting: f64, constraint: f64);
170impl_numeric_value_sequence_constraint!(setting: i64, constraint: u64);
171impl_numeric_value_sequence_constraint!(setting: i64, constraint: f64);
172impl_numeric_value_sequence_constraint!(setting: f64, constraint: u64);
173
174#[cfg(test)]
175mod tests;