webrtc_constraints/algorithms/fitness_distance/value_constraint.rs
1use crate::constraint::ResolvedValueConstraint;
2
3use super::{
4 setting::SettingFitnessDistanceError, FitnessDistance, SettingFitnessDistanceErrorKind,
5};
6
7// Standard implementation for value constraints of arbitrary `Setting` and `Constraint`
8// types where `Setting: PartialEq<Constraint>`:
9macro_rules! impl_non_numeric_value_constraint {
10 (setting: $s:ty, constraint: $c:ty) => {
11 impl<'a> FitnessDistance<Option<&'a $s>> for ResolvedValueConstraint<$c>
12 where
13 $s: PartialEq<$c>,
14 {
15 type Error = SettingFitnessDistanceError;
16
17 fn fitness_distance(&self, setting: Option<&'a $s>) -> Result<f64, Self::Error> {
18 if let Some(exact) = self.exact.as_ref() {
19 // As specified in step 2 of the `fitness distance` algorithm:
20 // <https://www.w3.org/TR/mediacapture-streams/#dfn-fitness-distance>
21 //
22 // > If the constraint is required (constraintValue either contains
23 // > one or more members named […] 'exact' […]), and the settings
24 // > dictionary's constraintName member's value does not satisfy the
25 // > constraint or doesn't exist, the fitness distance is positive infinity.
26 match setting {
27 Some(actual) if actual == exact => {}
28 Some(setting) => {
29 return Err(SettingFitnessDistanceError {
30 kind: SettingFitnessDistanceErrorKind::Mismatch,
31 constraint: format!("{}", self.to_required_only()),
32 setting: Some(format!("{:?}", setting)),
33 })
34 }
35 None => {
36 return Err(SettingFitnessDistanceError {
37 kind: SettingFitnessDistanceErrorKind::Missing,
38 constraint: format!("{}", self.to_required_only()),
39 setting: None,
40 })
41 }
42 };
43 }
44
45 if let Some(ideal) = self.ideal.as_ref() {
46 match setting {
47 Some(actual) if actual == ideal => {
48 // As specified in step 8 of the `fitness distance` algorithm:
49 // <https://www.w3.org/TR/mediacapture-streams/#dfn-fitness-distance>
50 //
51 // > For all string, enum and boolean constraints […],
52 // > the fitness distance is the result of the formula:
53 // >
54 // > ```
55 // > (actual == ideal) ? 0 : 1
56 // > ```
57 Ok(0.0)
58 }
59 _ => {
60 // As specified in step 5 of the `fitness distance` algorithm:
61 // <https://www.w3.org/TR/mediacapture-streams/#dfn-fitness-distance>
62 //
63 // > If the settings dictionary's `constraintName` member
64 // > does not exist, the fitness distance is 1.
65 Ok(1.0)
66 }
67 }
68 } else {
69 // As specified in step 6 of the `fitness distance` algorithm:
70 // <https://www.w3.org/TR/mediacapture-streams/#dfn-fitness-distance>
71 //
72 // > If no ideal value is specified (constraintValue either
73 // > contains no member named 'ideal', or, if bare values are to be
74 // > treated as 'ideal', isn't a bare value), the fitness distance is 0.
75 Ok(0.0)
76 }
77 }
78 }
79 };
80}
81
82impl_non_numeric_value_constraint!(setting: bool, constraint: bool);
83impl_non_numeric_value_constraint!(setting: String, constraint: String);
84
85// Specialized implementations for floating-point value constraints (and settings):
86
87macro_rules! impl_numeric_value_constraint {
88 (setting: $s:ty, constraint: $c:ty) => {
89 impl<'a> FitnessDistance<Option<&'a $s>> for ResolvedValueConstraint<$c> {
90 type Error = SettingFitnessDistanceError;
91
92 fn fitness_distance(&self, setting: Option<&'a $s>) -> Result<f64, Self::Error> {
93 if let Some(exact) = self.exact {
94 // As specified in step 2 of the `fitness distance` algorithm:
95 // <https://www.w3.org/TR/mediacapture-streams/#dfn-fitness-distance>
96 //
97 // > If the constraint is required (constraintValue either contains
98 // > one or more members named […] 'exact' […]), and the settings
99 // > dictionary's constraintName member's value does not satisfy the
100 // > constraint or doesn't exist, the fitness distance is positive infinity.
101 match setting {
102 Some(&actual) if super::is_nearly_equal_to(actual as f64, exact as f64) => {
103 }
104 Some(setting) => {
105 return Err(SettingFitnessDistanceError {
106 kind: SettingFitnessDistanceErrorKind::Mismatch,
107 constraint: format!("{}", self.to_required_only()),
108 setting: Some(format!("{:?}", setting)),
109 })
110 }
111 None => {
112 return Err(SettingFitnessDistanceError {
113 kind: SettingFitnessDistanceErrorKind::Missing,
114 constraint: format!("{}", self.to_required_only()),
115 setting: None,
116 })
117 }
118 };
119 }
120
121 if let Some(ideal) = self.ideal {
122 match setting {
123 Some(&actual) => {
124 let actual: f64 = actual as f64;
125 let ideal: f64 = ideal as f64;
126 // As specified in step 7 of the `fitness distance` algorithm:
127 // <https://www.w3.org/TR/mediacapture-streams/#dfn-fitness-distance>
128 //
129 // > For all positive numeric constraints […],
130 // > the fitness distance is the result of the formula
131 // >
132 // > ```
133 // > (actual == ideal) ? 0 : |actual - ideal| / max(|actual|, |ideal|)
134 // > ```
135 Ok(super::relative_fitness_distance(actual, ideal))
136 }
137 None => {
138 // As specified in step 5 of the `fitness distance` algorithm:
139 // <https://www.w3.org/TR/mediacapture-streams/#dfn-fitness-distance>
140 //
141 // > If the settings dictionary's `constraintName` member
142 // > does not exist, the fitness distance is 1.
143 Ok(1.0)
144 }
145 }
146 } else {
147 // As specified in step 6 of the `fitness distance` algorithm:
148 // <https://www.w3.org/TR/mediacapture-streams/#dfn-fitness-distance>
149 //
150 // > If no ideal value is specified (constraintValue either
151 // > contains no member named 'ideal', or, if bare values are to be
152 // > treated as 'ideal', isn't a bare value), the fitness distance is 0.
153 Ok(0.0)
154 }
155 }
156 }
157 };
158}
159
160impl_numeric_value_constraint!(setting: f64, constraint: f64);
161impl_numeric_value_constraint!(setting: i64, constraint: u64);
162impl_numeric_value_constraint!(setting: i64, constraint: f64);
163impl_numeric_value_constraint!(setting: f64, constraint: u64);
164
165// Specialized implementations for boolean value constraints of mis-matching
166// and thus either "existence"-checked or ignored setting types:
167macro_rules! impl_exists_value_constraint {
168 (settings: [$($s:ty),+], constraint: bool) => {
169 $(impl_exists_value_constraint!(setting: $s, constraint: bool);)+
170 };
171 (setting: $s:ty, constraint: bool) => {
172 impl<'a> FitnessDistance<Option<&'a $s>> for ResolvedValueConstraint<bool> {
173 type Error = SettingFitnessDistanceError;
174
175 fn fitness_distance(&self, setting: Option<&'a $s>) -> Result<f64, Self::Error> {
176 // A bare boolean value (as described in step 4 of the
177 // `fitness distance` algorithm) gets parsed as:
178 // ```
179 // ResolvedValueConstraint::<bool> {
180 // exact: Some(bare),
181 // ideal: None,
182 // }
183 // ```
184 //
185 // For all other configurations we just interpret it as an incompatible constraint.
186 match self.exact {
187 // As specified in step 4 of the `fitness distance` algorithm:
188 // <https://www.w3.org/TR/mediacapture-streams/#dfn-fitness-distance>
189 //
190 // > If constraintValue is a boolean, but the constrainable property is not,
191 // > then the fitness distance is based on whether the settings dictionary's
192 // > `constraintName` member exists or not, from the formula:
193 // >
194 // > ```
195 // > (constraintValue == exists) ? 0 : 1
196 // > ```
197 Some(expected) => {
198 if setting.is_some() == expected {
199 Ok(0.0)
200 } else {
201 Ok(1.0)
202 }
203 }
204 // As specified in step 3 of the `fitness distance` algorithm:
205 // <https://www.w3.org/TR/mediacapture-streams/#dfn-fitness-distance>
206 //
207 // > If the constraint does not apply for this type of object,
208 // > the fitness distance is 0 (that is, the constraint does not
209 // > influence the fitness distance).
210 None => Ok(0.0),
211 }
212 }
213 }
214 };
215}
216
217impl_exists_value_constraint!(settings: [String, i64, f64], constraint: bool);
218
219#[cfg(test)]
220mod tests;