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;