1pub type CIResult<T> = Result<T, CIError>;
14
15pub use crate::interval::IntervalError;
16
17use num_traits::Float;
18
19#[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 #[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#[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
85pub(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
105pub(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}