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;