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