xref: /webrtc/constraints/src/errors.rs (revision 5d8fe953)
1cc088ae5SVincent Esche //! Errors, as defined in the ["Media Capture and Streams"][mediacapture_streams] spec.
2cc088ae5SVincent Esche //!
3cc088ae5SVincent Esche //! [mediacapture_streams]: https://www.w3.org/TR/mediacapture-streams/
4cc088ae5SVincent Esche 
56209c8acSVincent Esche use std::collections::HashMap;
66209c8acSVincent Esche 
791a9d9aeSVincent Esche use crate::{
891a9d9aeSVincent Esche     algorithms::{ConstraintFailureInfo, SettingFitnessDistanceErrorKind},
991a9d9aeSVincent Esche     MediaTrackProperty,
1091a9d9aeSVincent Esche };
116209c8acSVincent Esche 
1286143b07SVincent Esche /// An error indicating one or more over-constrained settings.
1391a9d9aeSVincent Esche #[derive(Clone, Eq, PartialEq, Debug)]
14cc088ae5SVincent Esche pub struct OverconstrainedError {
15cc088ae5SVincent Esche     /// The offending constraint's name.
1691a9d9aeSVincent Esche     pub constraint: MediaTrackProperty,
1786143b07SVincent Esche     /// An error message, or `None` if exposure-mode was `Protected`.
18cc088ae5SVincent Esche     pub message: Option<String>,
19cc088ae5SVincent Esche }
20cc088ae5SVincent Esche 
2191a9d9aeSVincent Esche impl Default for OverconstrainedError {
default() -> Self2291a9d9aeSVincent Esche     fn default() -> Self {
2391a9d9aeSVincent Esche         Self {
2491a9d9aeSVincent Esche             constraint: MediaTrackProperty::from(""),
2591a9d9aeSVincent Esche             message: Default::default(),
2691a9d9aeSVincent Esche         }
2791a9d9aeSVincent Esche     }
2891a9d9aeSVincent Esche }
2991a9d9aeSVincent Esche 
30cc088ae5SVincent Esche impl std::fmt::Display for OverconstrainedError {
fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result31cc088ae5SVincent Esche     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32cc088ae5SVincent Esche         write!(f, "Overconstrained property {:?}", self.constraint)?;
33cc088ae5SVincent Esche         if let Some(message) = self.message.as_ref() {
34*5d8fe953SJoão Oliveira             write!(f, ": {message}")?;
35cc088ae5SVincent Esche         }
36cc088ae5SVincent Esche         Ok(())
37cc088ae5SVincent Esche     }
38cc088ae5SVincent Esche }
39cc088ae5SVincent Esche 
40cc088ae5SVincent Esche impl std::error::Error for OverconstrainedError {}
416209c8acSVincent Esche 
426209c8acSVincent Esche impl OverconstrainedError {
exposing_device_information( failed_constraints: HashMap<MediaTrackProperty, ConstraintFailureInfo>, ) -> Self436209c8acSVincent Esche     pub(super) fn exposing_device_information(
4491a9d9aeSVincent Esche         failed_constraints: HashMap<MediaTrackProperty, ConstraintFailureInfo>,
456209c8acSVincent Esche     ) -> Self {
466209c8acSVincent Esche         let failed_constraint = failed_constraints
476209c8acSVincent Esche             .into_iter()
486209c8acSVincent Esche             .max_by_key(|(_, failure_info)| failure_info.failures);
496209c8acSVincent Esche 
506209c8acSVincent Esche         let (constraint, failure_info) =
516209c8acSVincent Esche             failed_constraint.expect("Empty candidates implies non-empty failed constraints");
526209c8acSVincent Esche 
536209c8acSVincent Esche         struct Violation {
546209c8acSVincent Esche             constraint: String,
556209c8acSVincent Esche             settings: Vec<String>,
566209c8acSVincent Esche         }
576209c8acSVincent Esche         let mut violators_by_kind: HashMap<SettingFitnessDistanceErrorKind, Violation> =
586209c8acSVincent Esche             HashMap::default();
596209c8acSVincent Esche 
606209c8acSVincent Esche         for error in failure_info.errors {
616209c8acSVincent Esche             let violation = violators_by_kind.entry(error.kind).or_insert(Violation {
626209c8acSVincent Esche                 constraint: error.constraint.clone(),
636209c8acSVincent Esche                 settings: vec![],
646209c8acSVincent Esche             });
656209c8acSVincent Esche             assert_eq!(violation.constraint, error.constraint);
666209c8acSVincent Esche             if let Some(setting) = error.setting {
676209c8acSVincent Esche                 violation.settings.push(setting.clone());
686209c8acSVincent Esche             }
696209c8acSVincent Esche         }
706209c8acSVincent Esche 
716209c8acSVincent Esche         let formatted_reasons: Vec<_> = violators_by_kind
726209c8acSVincent Esche             .into_iter()
736209c8acSVincent Esche             .map(|(kind, violation)| {
746209c8acSVincent Esche                 let kind_str = match kind {
756209c8acSVincent Esche                     SettingFitnessDistanceErrorKind::Missing => "missing",
766209c8acSVincent Esche                     SettingFitnessDistanceErrorKind::Mismatch => "a mismatch",
776209c8acSVincent Esche                     SettingFitnessDistanceErrorKind::TooSmall => "too small",
786209c8acSVincent Esche                     SettingFitnessDistanceErrorKind::TooLarge => "too large",
796209c8acSVincent Esche                 };
806209c8acSVincent Esche 
816209c8acSVincent Esche                 let mut settings = violation.settings;
826209c8acSVincent Esche 
836209c8acSVincent Esche                 if settings.is_empty() {
846209c8acSVincent Esche                     return format!("{} (does not satisfy {})", kind_str, violation.constraint);
856209c8acSVincent Esche                 }
866209c8acSVincent Esche 
876209c8acSVincent Esche                 settings.sort();
886209c8acSVincent Esche 
896209c8acSVincent Esche                 format!(
906209c8acSVincent Esche                     "{} ([{}] do not satisfy {})",
916209c8acSVincent Esche                     kind_str,
926209c8acSVincent Esche                     settings.join(", "),
936209c8acSVincent Esche                     violation.constraint
946209c8acSVincent Esche                 )
956209c8acSVincent Esche             })
966209c8acSVincent Esche             .collect();
976209c8acSVincent Esche 
986209c8acSVincent Esche         let formatted_reason = match &formatted_reasons[..] {
996209c8acSVincent Esche             [] => unreachable!(),
1006209c8acSVincent Esche             [reason] => reason.clone(),
1016209c8acSVincent Esche             [reasons @ .., reason] => {
1026209c8acSVincent Esche                 let reasons = reasons.join(", ");
103*5d8fe953SJoão Oliveira                 format!("either {reasons}, or {reason}")
1046209c8acSVincent Esche             }
1056209c8acSVincent Esche         };
106*5d8fe953SJoão Oliveira         let message = Some(format!("Setting was {formatted_reason}."));
1076209c8acSVincent Esche 
1086209c8acSVincent Esche         Self {
1096209c8acSVincent Esche             constraint,
1106209c8acSVincent Esche             message,
1116209c8acSVincent Esche         }
1126209c8acSVincent Esche     }
1136209c8acSVincent Esche }
114