webrtc_constraints/
errors.rs

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
5use std::collections::HashMap;
6
7use 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)]
14pub 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
21impl Default for OverconstrainedError {
22    fn default() -> Self {
23        Self {
24            constraint: MediaTrackProperty::from(""),
25            message: Default::default(),
26        }
27    }
28}
29
30impl std::fmt::Display for OverconstrainedError {
31    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
40impl std::error::Error for OverconstrainedError {}
41
42impl OverconstrainedError {
43    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 {}, or {}", reasons, reason)
104            }
105        };
106        let message = Some(format!("Setting was {}.", formatted_reason));
107
108        Self {
109            constraint,
110            message,
111        }
112    }
113}