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