gam_problem/
custom_family_error.rs1use thiserror::Error;
4
5use crate::{IdentifiabilityAudit, MapUniquenessError};
6
7#[derive(Debug, Clone, Error)]
8pub enum CustomFamilyError {
9 #[error("custom-family invalid input in {context}: {reason}")]
10 InvalidInput {
11 context: &'static str,
12 reason: String,
13 },
14 #[error("custom-family optimization error in {context}: {reason}")]
15 Optimization {
16 context: &'static str,
17 reason: String,
18 },
19 #[error("{reason}")]
20 DimensionMismatch { reason: String },
21 #[error("{reason}")]
22 NumericalFailure { reason: String },
23 #[error("{reason}")]
24 ConstraintViolation { reason: String },
25 #[error("{reason}")]
26 UnsupportedConfiguration { reason: String },
27 #[error("{reason}")]
28 BasisDecompositionFailed { reason: String },
29 #[error("identifiability audit refused the fit: {}", audit.summary)]
39 IdentifiabilityFailure { audit: IdentifiabilityAudit },
40 #[error("MAP estimate non-unique: {}", error)]
47 MapUniquenessFailure { error: MapUniquenessError },
48}
49
50impl From<String> for CustomFamilyError {
51 fn from(value: String) -> Self {
52 Self::InvalidInput {
53 context: "custom-family string boundary",
54 reason: value,
55 }
56 }
57}
58
59impl From<CustomFamilyError> for String {
60 fn from(value: CustomFamilyError) -> Self {
61 value.to_string()
62 }
63}
64
65#[cfg(test)]
66mod tests {
67 use super::*;
68
69 #[test]
70 fn invalid_input_display_contains_context_and_reason() {
71 let err = CustomFamilyError::InvalidInput {
72 context: "my_context",
73 reason: "something broke".to_string(),
74 };
75 let msg = err.to_string();
76 assert!(msg.contains("my_context"), "message: {msg}");
77 assert!(msg.contains("something broke"), "message: {msg}");
78 }
79
80 #[test]
81 fn optimization_display_contains_context_and_reason() {
82 let err = CustomFamilyError::Optimization {
83 context: "outer_loop",
84 reason: "diverged".to_string(),
85 };
86 let msg = err.to_string();
87 assert!(
88 msg.contains("outer_loop") && msg.contains("diverged"),
89 "message: {msg}"
90 );
91 }
92
93 #[test]
94 fn dimension_mismatch_displays_reason() {
95 let err = CustomFamilyError::DimensionMismatch {
96 reason: "3 vs 4".to_string(),
97 };
98 assert_eq!(err.to_string(), "3 vs 4");
99 }
100
101 #[test]
102 fn numerical_failure_displays_reason() {
103 let err = CustomFamilyError::NumericalFailure {
104 reason: "NaN detected".to_string(),
105 };
106 assert_eq!(err.to_string(), "NaN detected");
107 }
108
109 #[test]
110 fn from_string_creates_invalid_input_with_boundary_context() {
111 let err = CustomFamilyError::from("string error".to_string());
112 assert!(matches!(err, CustomFamilyError::InvalidInput { .. }));
113 assert!(err.to_string().contains("string error"));
114 }
115
116 #[test]
117 fn from_custom_family_error_for_string_uses_display() {
118 let err = CustomFamilyError::NumericalFailure {
119 reason: "singular".to_string(),
120 };
121 let s = String::from(err);
122 assert_eq!(s, "singular");
123 }
124}