stats_ci/
error.rs

1//!
2//! Error types and conversion traits
3//!
4//! The crate defines a type [`CIError`] to represent errors that can occur during the computation of confidence intervals.
5//! The type [`CIResult<T>`] is a type alias for [`Result<T, CIError>`].
6//!
7
8///
9/// Result type for confidence interval computations.
10///
11/// This type is a type alias for [`Result<T, CIError>`].
12///  
13pub type CIResult<T> = Result<T, CIError>;
14
15pub use crate::interval::IntervalError;
16
17use num_traits::Float;
18
19///
20/// Error types for confidence interval computations.
21///
22#[allow(missing_docs)]
23#[derive(thiserror::Error, Debug)]
24pub enum CIError {
25    #[error("Too few samples to compute: {0}")]
26    TooFewSamples(usize),
27
28    #[error("Too few successes: {0} (population: {1}; n*p={2}))")]
29    TooFewSuccesses(usize, usize, f64),
30
31    #[error("Too few failures: {0} (population: {1}; n*q={2}))")]
32    TooFewFailures(usize, usize, f64),
33
34    #[error("Invalid confidence level (must be )): {0}")]
35    InvalidConfidenceLevel(f64),
36
37    #[error("Invalid quantile (must be in (0, 1)): {0}")]
38    InvalidQuantile(f64),
39
40    #[error("Invalid number of successes: {0} (population: {1})")]
41    InvalidSuccesses(usize, usize),
42
43    #[error("Geometric/harmonic mean require strictly positive values: found {0}")]
44    NonPositiveValue(f64),
45
46    #[error("Invalid input data found")]
47    InvalidInputData,
48
49    #[error("Float type conversion error: {0}")]
50    FloatConversionError(String),
51
52    #[error("Index error: {0} should be in [0, {1})")]
53    IndexError(f64, usize),
54
55    // wrapper errors
56    #[error("String error: {0}")]
57    Error(String),
58
59    #[error("Interval error: {0}")]
60    IntervalError(#[from] IntervalError),
61
62    #[error("Different sample sizes: {0} vs. {1}")]
63    DifferentSampleSizes(usize, usize),
64}
65
66impl From<&str> for CIError {
67    fn from(s: &str) -> Self {
68        CIError::Error(s.to_string())
69    }
70}
71
72///
73/// Error types for conversion from a generic [`Float`] type to a [`CIResult<f64>`].
74///
75#[allow(missing_docs)]
76#[derive(thiserror::Error, Debug)]
77pub enum ConversionError {
78    #[error("Empty interval has no concrete bounds")]
79    NoConcreteBoundsError,
80
81    #[error("Degenerate interval has single bound")]
82    SingleBoundError,
83}
84
85///
86/// Decorator trait used to convert from a generic [`Float`] type to a [`CIResult<f64>`]
87///
88pub(crate) trait FloatConversion<F: Float> {
89    fn try_f64(&self, var_name: &str) -> CIResult<f64>;
90}
91
92impl<F: Float> FloatConversion<F> for F {
93    #[inline]
94    fn try_f64(&self, var_name: &str) -> CIResult<f64> {
95        self.to_f64().ok_or_else(|| {
96            CIError::FloatConversionError(format!(
97                "Error converting {} ({}) to f64",
98                var_name,
99                std::any::type_name::<F>()
100            ))
101        })
102    }
103}
104
105///
106/// Decorator trait used to convert from an [`Option<F>`] to a [`CIResult<F>`]
107///
108pub(crate) trait FloatReverseConversion<F: num_traits::Float> {
109    fn convert(&self, var_name: &str) -> CIResult<F>;
110}
111
112impl<F: Float> FloatReverseConversion<F> for Option<F> {
113    #[inline]
114    fn convert(&self, var_name: &str) -> CIResult<F> {
115        self.ok_or_else(|| {
116            CIError::FloatConversionError(format!(
117                "Error converting {} to {}",
118                var_name,
119                std::any::type_name::<F>()
120            ))
121        })
122    }
123}
124
125#[cfg(test)]
126mod tests {
127    use super::*;
128
129    fn string_to_error() -> CIResult<()> {
130        Err("This is a string error")?
131    }
132
133    #[test]
134    fn test_string_to_error() {
135        let err = string_to_error();
136        assert!(err.is_err());
137        match err {
138            Err(CIError::Error(s)) => assert_eq!(s, "This is a string error"),
139            Err(e) => panic!("Unexpected error type: {:?}", e),
140            Ok(_) => panic!("Unexpected success"),
141        }
142    }
143}