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