xref: /webrtc/constraints/src/errors.rs (revision addcf23e)
1 //! Errors, as defined in the ["Media Capture and Streams"][mediacapture_streams] spec.
2 //!
3 //! [mediacapture_streams]: https://www.w3.org/TR/mediacapture-streams/
4 
5 use std::collections::HashMap;
6 
7 use crate::algorithms::{ConstraintFailureInfo, SettingFitnessDistanceErrorKind};
8 
9 #[derive(Clone, Default, Eq, PartialEq, Debug)]
10 pub struct OverconstrainedError {
11     /// The offending constraint's name.
12     pub constraint: String,
13     /// Error message.
14     pub message: Option<String>,
15 }
16 
17 impl std::fmt::Display for OverconstrainedError {
18     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
19         write!(f, "Overconstrained property {:?}", self.constraint)?;
20         if let Some(message) = self.message.as_ref() {
21             write!(f, ": {}", message)?;
22         }
23         Ok(())
24     }
25 }
26 
27 impl std::error::Error for OverconstrainedError {}
28 
29 impl OverconstrainedError {
30     pub(super) fn exposing_device_information(
31         failed_constraints: HashMap<String, ConstraintFailureInfo>,
32     ) -> Self {
33         let failed_constraint = failed_constraints
34             .into_iter()
35             .max_by_key(|(_, failure_info)| failure_info.failures);
36 
37         let (constraint, failure_info) =
38             failed_constraint.expect("Empty candidates implies non-empty failed constraints");
39 
40         struct Violation {
41             constraint: String,
42             settings: Vec<String>,
43         }
44         let mut violators_by_kind: HashMap<SettingFitnessDistanceErrorKind, Violation> =
45             HashMap::default();
46 
47         for error in failure_info.errors {
48             let violation = violators_by_kind.entry(error.kind).or_insert(Violation {
49                 constraint: error.constraint.clone(),
50                 settings: vec![],
51             });
52             assert_eq!(violation.constraint, error.constraint);
53             if let Some(setting) = error.setting {
54                 violation.settings.push(setting.clone());
55             }
56         }
57 
58         let formatted_reasons: Vec<_> = violators_by_kind
59             .into_iter()
60             .map(|(kind, violation)| {
61                 let kind_str = match kind {
62                     SettingFitnessDistanceErrorKind::Missing => "missing",
63                     SettingFitnessDistanceErrorKind::Mismatch => "a mismatch",
64                     SettingFitnessDistanceErrorKind::TooSmall => "too small",
65                     SettingFitnessDistanceErrorKind::TooLarge => "too large",
66                 };
67 
68                 let mut settings = violation.settings;
69 
70                 if settings.is_empty() {
71                     return format!("{} (does not satisfy {})", kind_str, violation.constraint);
72                 }
73 
74                 settings.sort();
75 
76                 format!(
77                     "{} ([{}] do not satisfy {})",
78                     kind_str,
79                     settings.join(", "),
80                     violation.constraint
81                 )
82             })
83             .collect();
84 
85         let formatted_reason = match &formatted_reasons[..] {
86             [] => unreachable!(),
87             [reason] => reason.clone(),
88             [reasons @ .., reason] => {
89                 let reasons = reasons.join(", ");
90                 format!("either {}, or {}", reasons, reason)
91             }
92         };
93         let message = Some(format!("Setting was {}.", formatted_reason));
94 
95         Self {
96             constraint,
97             message,
98         }
99     }
100 }
101