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!(msg.contains("outer_loop") && msg.contains("diverged"), "message: {msg}");
88 }
89
90 #[test]
91 fn dimension_mismatch_displays_reason() {
92 let err = CustomFamilyError::DimensionMismatch { reason: "3 vs 4".to_string() };
93 assert_eq!(err.to_string(), "3 vs 4");
94 }
95
96 #[test]
97 fn numerical_failure_displays_reason() {
98 let err = CustomFamilyError::NumericalFailure { reason: "NaN detected".to_string() };
99 assert_eq!(err.to_string(), "NaN detected");
100 }
101
102 #[test]
103 fn from_string_creates_invalid_input_with_boundary_context() {
104 let err = CustomFamilyError::from("string error".to_string());
105 assert!(matches!(err, CustomFamilyError::InvalidInput { .. }));
106 assert!(err.to_string().contains("string error"));
107 }
108
109 #[test]
110 fn from_custom_family_error_for_string_uses_display() {
111 let err = CustomFamilyError::NumericalFailure { reason: "singular".to_string() };
112 let s = String::from(err);
113 assert_eq!(s, "singular");
114 }
115}