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