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;