1use hpr_aero::AeroError;
4use hpr_atmos::AtmosError;
5use hpr_core::CoreError;
6use hpr_design::{DesignError, Finding, Severity};
7use thiserror::Error;
8
9use crate::integrator::{IntegrationError, SettingsError};
10
11#[derive(Debug, Error)]
13#[non_exhaustive]
14pub enum SimError {
15 #[error("{what} is outside its domain: {value}")]
17 Domain {
18 what: &'static str,
20 value: f64,
22 },
23 #[error("unsupported: {what}")]
25 Unsupported {
26 what: &'static str,
28 },
29 #[error("{what}: `{component}`")]
32 Parting {
33 what: &'static str,
35 component: String,
37 },
38 #[error("{what}: `{component}`")]
40 Shift {
41 what: &'static str,
43 component: String,
45 },
46 #[error("{what}: `{component}`")]
48 MassRelease {
49 what: &'static str,
51 component: String,
53 },
54 #[error(
57 "the design has {} error finding(s); the first is {}",
58 error_count(.0),
59 first_error(.0)
60 )]
61 DesignChecks(Vec<Finding>),
62 #[error(transparent)]
64 Design(#[from] DesignError),
65 #[error(transparent)]
68 Aero(#[from] AeroError),
69 #[error(transparent)]
71 Atmosphere(#[from] AtmosError),
72 #[error(transparent)]
74 Core(#[from] CoreError),
75 #[error(transparent)]
77 Settings(#[from] SettingsError),
78 #[error("the integration failed")]
80 Integration(#[source] Box<IntegrationError<SimError>>),
81}
82
83fn error_count(findings: &[Finding]) -> usize {
85 findings
86 .iter()
87 .filter(|finding| finding.severity() == Severity::Error)
88 .count()
89}
90
91fn first_error(findings: &[Finding]) -> String {
93 findings
94 .iter()
95 .find(|finding| finding.severity() == Severity::Error)
96 .map_or_else(|| "missing".to_owned(), |finding| format!("{finding:?}"))
97}
98
99#[cfg(test)]
100mod tests {
101 use super::*;
102
103 #[test]
105 fn design_checks_message_counts_and_names_only_errors() {
106 let warning = Finding::MotorPastMountTop {
107 configuration: "c".to_owned(),
108 mount: "mount".to_owned(),
109 excess_m: 0.01,
110 };
111 let error = Finding::MotorWiderThanMount {
112 configuration: "c".to_owned(),
113 mount: "mount".to_owned(),
114 motor_diameter_m: 0.038,
115 mount_inner_diameter_m: 0.029,
116 };
117 let message = SimError::DesignChecks(vec![warning, error.clone()]).to_string();
118 assert_eq!(
119 message,
120 format!("the design has 1 error finding(s); the first is {error:?}")
121 );
122 }
123}