webrtc_constraints/algorithms/fitness_distance/
value_range_constraint.rs

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