Skip to main content

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}