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