stats_claw/error.rs
1//! Typed error and `Result` alias for the framework's fallible numerics.
2//!
3//! This module defines the crate-wide `Error` enum and the `Result<T>` alias
4//! that all hand-written numerics return on bad input or domain violations
5//! (e.g. a negative scale parameter, an empty sample). It is the single place
6//! recoverable failures are modelled, so call sites never reach for `unwrap`.
7
8use std::fmt;
9
10/// A recoverable failure raised by the framework's hand-written numerics.
11///
12/// Numerics never panic on bad input — they return one of these variants so the
13/// caller can decide how to recover. The message-carrying variants embed a short
14/// human-readable reason; the unit variants are self-describing.
15#[derive(Debug, Clone, PartialEq, Eq)]
16pub enum Error {
17 /// A required sample or collection was empty.
18 EmptyInput,
19 /// A parameter or value violated a precondition; carries the reason.
20 InvalidInput(String),
21 /// The input is technically valid but degenerate (e.g. zero variance);
22 /// carries the reason.
23 DegenerateInput(String),
24 /// There were too few observations to compute the requested quantity.
25 InsufficientData,
26 /// An iterative procedure exhausted its budget without converging.
27 NotConverged,
28}
29
30impl fmt::Display for Error {
31 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32 match self {
33 Self::EmptyInput => write!(f, "input was empty"),
34 Self::InvalidInput(m) => write!(f, "invalid input: {m}"),
35 Self::DegenerateInput(m) => write!(f, "degenerate input: {m}"),
36 Self::InsufficientData => write!(f, "insufficient data"),
37 Self::NotConverged => write!(f, "did not converge"),
38 }
39 }
40}
41
42impl std::error::Error for Error {}
43
44/// Result alias for the framework's fallible numerics, defaulting the error to
45/// [`Error`].
46pub type Result<T> = std::result::Result<T, Error>;
47
48#[cfg(test)]
49mod tests {
50 use super::*;
51
52 #[test]
53 fn display_is_human_readable() {
54 assert_eq!(
55 Error::EmptyInput.to_string(),
56 "input was empty",
57 "EmptyInput display text changed"
58 );
59 }
60
61 #[test]
62 fn display_includes_detail_for_message_variants() {
63 assert_eq!(
64 Error::InvalidInput("scale must be > 0".to_owned()).to_string(),
65 "invalid input: scale must be > 0"
66 );
67 assert_eq!(
68 Error::DegenerateInput("zero variance".to_owned()).to_string(),
69 "degenerate input: zero variance"
70 );
71 }
72
73 #[test]
74 fn usable_as_std_error() {
75 fn boxed() -> Box<dyn std::error::Error> {
76 Box::new(Error::NotConverged)
77 }
78 assert_eq!(boxed().to_string(), "did not converge");
79 }
80}