1use thiserror::Error;
7
8#[derive(Error, Debug, Clone, PartialEq)]
25pub enum StatsError {
26 #[error("Invalid input: {message}")]
28 InvalidInput {
29 message: String,
31 },
32
33 #[error("Conversion error: {message}")]
35 ConversionError {
36 message: String,
38 },
39
40 #[error("Empty data: {message}")]
42 EmptyData {
43 message: String,
45 },
46
47 #[error("Dimension mismatch: {message}")]
49 DimensionMismatch {
50 message: String,
52 },
53
54 #[error("Numerical error: {message}")]
56 NumericalError {
57 message: String,
59 },
60
61 #[error("Model not fitted: {message}")]
63 NotFitted {
64 message: String,
66 },
67
68 #[error("Invalid parameter: {message}")]
70 InvalidParameter {
71 message: String,
73 },
74
75 #[error("Index out of bounds: {message}")]
77 IndexOutOfBounds {
78 message: String,
80 },
81
82 #[error("Mathematical error: {message}")]
84 MathematicalError {
85 message: String,
87 },
88}
89
90pub type StatsResult<T> = Result<T, StatsError>;
104
105impl StatsError {
106 pub fn invalid_input<S: Into<String>>(message: S) -> Self {
108 StatsError::InvalidInput {
109 message: message.into(),
110 }
111 }
112
113 pub fn conversion_error<S: Into<String>>(message: S) -> Self {
115 StatsError::ConversionError {
116 message: message.into(),
117 }
118 }
119
120 pub fn empty_data<S: Into<String>>(message: S) -> Self {
122 StatsError::EmptyData {
123 message: message.into(),
124 }
125 }
126
127 pub fn dimension_mismatch<S: Into<String>>(message: S) -> Self {
129 StatsError::DimensionMismatch {
130 message: message.into(),
131 }
132 }
133
134 pub fn numerical_error<S: Into<String>>(message: S) -> Self {
136 StatsError::NumericalError {
137 message: message.into(),
138 }
139 }
140
141 pub fn not_fitted<S: Into<String>>(message: S) -> Self {
143 StatsError::NotFitted {
144 message: message.into(),
145 }
146 }
147
148 pub fn invalid_parameter<S: Into<String>>(message: S) -> Self {
150 StatsError::InvalidParameter {
151 message: message.into(),
152 }
153 }
154
155 pub fn index_out_of_bounds<S: Into<String>>(message: S) -> Self {
157 StatsError::IndexOutOfBounds {
158 message: message.into(),
159 }
160 }
161
162 pub fn mathematical_error<S: Into<String>>(message: S) -> Self {
164 StatsError::MathematicalError {
165 message: message.into(),
166 }
167 }
168}
169
170#[cfg(test)]
171mod tests {
172 use super::*;
173
174 #[test]
176 fn test_all_variants_display() {
177 let cases = vec![
178 (StatsError::invalid_input("msg"), "Invalid input: msg"),
179 (StatsError::conversion_error("msg"), "Conversion error: msg"),
180 (StatsError::empty_data("msg"), "Empty data: msg"),
181 (
182 StatsError::dimension_mismatch("msg"),
183 "Dimension mismatch: msg",
184 ),
185 (StatsError::numerical_error("msg"), "Numerical error: msg"),
186 (StatsError::not_fitted("msg"), "Model not fitted: msg"),
187 (
188 StatsError::invalid_parameter("msg"),
189 "Invalid parameter: msg",
190 ),
191 (
192 StatsError::index_out_of_bounds("msg"),
193 "Index out of bounds: msg",
194 ),
195 (
196 StatsError::mathematical_error("msg"),
197 "Mathematical error: msg",
198 ),
199 ];
200
201 for (err, expected) in cases {
202 assert_eq!(err.to_string(), expected, "Display format mismatch");
203 }
204 }
205
206 #[test]
208 fn test_error_equality() {
209 let err1 = StatsError::invalid_input("message");
210 let err2 = StatsError::invalid_input("message");
211 let err3 = StatsError::invalid_input("different");
212 let err4 = StatsError::conversion_error("message");
213
214 assert_eq!(err1, err2, "Same variant and message should be equal");
215 assert_ne!(err1, err3, "Different messages should not be equal");
216 assert_ne!(err1, err4, "Different variants should not be equal");
217 }
218
219 #[test]
221 fn test_error_clone() {
222 let err = StatsError::conversion_error("test");
223 let cloned = err.clone();
224 assert_eq!(err, cloned);
225 }
226
227 #[test]
229 fn test_stats_result() {
230 let ok: StatsResult<f64> = Ok(42.0);
231 assert_eq!(ok.unwrap(), 42.0);
232
233 let err: StatsResult<f64> = Err(StatsError::invalid_input("test"));
234 assert!(err.is_err());
235 assert_eq!(err.unwrap_err(), StatsError::invalid_input("test"));
236 }
237
238 #[test]
240 fn test_helper_methods() {
241 assert!(matches!(
242 StatsError::invalid_input("msg"),
243 StatsError::InvalidInput { .. }
244 ));
245 assert!(matches!(
246 StatsError::conversion_error("msg"),
247 StatsError::ConversionError { .. }
248 ));
249 assert!(matches!(
250 StatsError::empty_data("msg"),
251 StatsError::EmptyData { .. }
252 ));
253 assert!(matches!(
254 StatsError::dimension_mismatch("msg"),
255 StatsError::DimensionMismatch { .. }
256 ));
257 assert!(matches!(
258 StatsError::numerical_error("msg"),
259 StatsError::NumericalError { .. }
260 ));
261 assert!(matches!(
262 StatsError::not_fitted("msg"),
263 StatsError::NotFitted { .. }
264 ));
265 assert!(matches!(
266 StatsError::invalid_parameter("msg"),
267 StatsError::InvalidParameter { .. }
268 ));
269 assert!(matches!(
270 StatsError::index_out_of_bounds("msg"),
271 StatsError::IndexOutOfBounds { .. }
272 ));
273 assert!(matches!(
274 StatsError::mathematical_error("msg"),
275 StatsError::MathematicalError { .. }
276 ));
277 }
278
279 #[test]
281 fn test_into_string_conversion() {
282 let err1 = StatsError::invalid_input("string slice");
284 assert_eq!(err1.to_string(), "Invalid input: string slice");
285
286 let err2 = StatsError::invalid_input("owned string".to_string());
288 assert_eq!(err2.to_string(), "Invalid input: owned string");
289 }
290
291 #[test]
293 fn test_error_propagation() {
294 fn might_fail() -> StatsResult<f64> {
295 Err(StatsError::invalid_input("test"))
296 }
297
298 fn propagate() -> StatsResult<f64> {
299 might_fail()?;
300 Ok(42.0)
301 }
302
303 let result = propagate();
304 assert!(result.is_err());
305 assert!(matches!(
306 result.unwrap_err(),
307 StatsError::InvalidInput { .. }
308 ));
309 }
310
311 #[test]
313 fn test_pattern_matching() {
314 let err = StatsError::conversion_error("failed");
315
316 match err {
317 StatsError::ConversionError { message } => {
318 assert_eq!(message, "failed");
319 }
320 _ => panic!("Wrong variant matched"),
321 }
322 }
323
324 #[test]
326 fn test_debug_implementation() {
327 let err = StatsError::invalid_input("test");
328 let debug_str = format!("{:?}", err);
329 assert!(debug_str.contains("InvalidInput"));
330 assert!(debug_str.contains("test"));
331 }
332}