battery_estimator/error.rs
1//! Error types for battery SOC estimation
2//!
3//! This module defines the error types that can occur during battery
4//! state-of-charge estimation operations.
5
6use thiserror::Error;
7
8/// Errors that can occur during battery SOC estimation
9///
10/// This enum represents all possible error conditions that may arise
11/// when using the battery estimator library.
12///
13/// # Examples
14///
15/// ```no_run
16/// use battery_estimator::{BatteryChemistry, SocEstimator, Error};
17///
18/// let estimator = SocEstimator::new(BatteryChemistry::LiPo);
19///
20/// match estimator.estimate_soc(3.7) {
21/// Ok(soc) => println!("SOC: {:.1}%", soc),
22/// Err(Error::InvalidCurve) => eprintln!("Invalid battery curve"),
23/// Err(Error::NumericalError) => eprintln!("Calculation error"),
24/// Err(Error::InvalidTemperature) => eprintln!("Invalid temperature"),
25/// }
26/// ```
27#[derive(Error, Debug, Clone, Copy, PartialEq, Eq, Hash)]
28pub enum Error {
29 /// The voltage curve data is invalid
30 ///
31 /// This error occurs when:
32 /// - The curve has fewer than 2 points (cannot interpolate)
33 /// - The curve points are not properly ordered
34 /// - The curve has duplicate voltage values
35 ///
36 /// # Examples
37 ///
38 /// ```no_run
39 /// use battery_estimator::{Curve, CurvePoint, Error};
40 ///
41 /// // Invalid: Only one point
42 /// let invalid_curve = Curve::new(&[CurvePoint::new(3.7, 50.0)]);
43 /// let result = invalid_curve.voltage_to_soc(3.7);
44 /// assert!(matches!(result, Err(Error::InvalidCurve)));
45 /// ```
46 #[error("Invalid voltage curve")]
47 InvalidCurve,
48
49 /// A numerical error occurred during calculation
50 ///
51 /// This error occurs when:
52 /// - Division by zero in interpolation
53 /// - Overflow or underflow in calculations
54 /// - Invalid floating-point operations
55 ///
56 /// # Examples
57 ///
58 /// ```no_run
59 /// use battery_estimator::{Curve, CurvePoint, Error};
60 ///
61 /// // This could occur if curve points have the same voltage
62 /// let problematic_curve = Curve::new(&[
63 /// CurvePoint::new(3.7, 50.0),
64 /// CurvePoint::new(3.7, 60.0), // Duplicate voltage!
65 /// ]);
66 /// ```
67 #[error("Numerical error in calculation")]
68 NumericalError,
69
70 /// The temperature value is invalid
71 ///
72 /// This error occurs when:
73 /// - Temperature is NaN (Not a Number)
74 /// - Temperature is infinity
75 /// - Temperature is outside reasonable bounds
76 ///
77 /// # Examples
78 ///
79 /// ```no_run
80 /// use battery_estimator::{BatteryChemistry, SocEstimator, Error};
81 ///
82 /// let estimator = SocEstimator::new(BatteryChemistry::LiPo);
83 ///
84 /// // Invalid temperature
85 /// let result = estimator.estimate_soc_with_temp(3.7, f32::NAN);
86 /// ```
87 #[error("Invalid temperature")]
88 InvalidTemperature,
89}
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94 use core::fmt::Write;
95
96 #[test]
97 fn test_error_display() {
98 // In no-std, Display is available via core::fmt
99 // We can test that the Display implementation compiles and works
100 let mut buffer = [0u8; 64];
101
102 // Test InvalidCurve
103 let mut writer = BufferWriter::new(&mut buffer);
104 write!(writer, "{}", Error::InvalidCurve).unwrap();
105 assert_eq!(writer.as_str(), "Invalid voltage curve");
106
107 // Test NumericalError
108 let mut writer = BufferWriter::new(&mut buffer);
109 write!(writer, "{}", Error::NumericalError).unwrap();
110 assert_eq!(writer.as_str(), "Numerical error in calculation");
111
112 // Test InvalidTemperature
113 let mut writer = BufferWriter::new(&mut buffer);
114 write!(writer, "{}", Error::InvalidTemperature).unwrap();
115 assert_eq!(writer.as_str(), "Invalid temperature");
116 }
117
118 #[test]
119 fn test_error_equality() {
120 assert_eq!(Error::InvalidCurve, Error::InvalidCurve);
121 assert_eq!(Error::NumericalError, Error::NumericalError);
122 assert_eq!(Error::InvalidTemperature, Error::InvalidTemperature);
123
124 assert_ne!(Error::InvalidCurve, Error::NumericalError);
125 }
126
127 #[test]
128 fn test_error_copy() {
129 let error1 = Error::InvalidCurve;
130 let error2 = error1;
131 assert_eq!(error1, error2);
132 }
133
134 #[test]
135 fn test_error_debug() {
136 let error = Error::NumericalError;
137 let mut buffer = [0u8; 64];
138 let mut writer = BufferWriter::new(&mut buffer);
139 write!(writer, "{:?}", error).unwrap();
140 assert!(writer.as_str().contains("NumericalError"));
141 }
142
143 #[test]
144 fn test_error_all_variants() {
145 // Test that all error variants can be created
146 let errors = [
147 Error::InvalidCurve,
148 Error::NumericalError,
149 Error::InvalidTemperature,
150 ];
151 assert_eq!(errors.len(), 3);
152 }
153
154 #[test]
155 fn test_error_variants_distinct() {
156 let error1 = Error::InvalidCurve;
157 let error2 = Error::NumericalError;
158 let error3 = Error::InvalidTemperature;
159
160 // Verify all variants are distinct
161 assert_ne!(error1, error2);
162 assert_ne!(error2, error3);
163 assert_ne!(error1, error3);
164 }
165
166 // Helper struct for testing Display in no-std
167 struct BufferWriter<'a> {
168 buffer: &'a mut [u8],
169 pos: usize,
170 }
171
172 impl<'a> BufferWriter<'a> {
173 fn new(buffer: &'a mut [u8]) -> Self {
174 BufferWriter { buffer, pos: 0 }
175 }
176
177 fn as_str(&self) -> &str {
178 core::str::from_utf8(&self.buffer[..self.pos]).unwrap()
179 }
180 }
181
182 impl<'a> core::fmt::Write for BufferWriter<'a> {
183 fn write_str(&mut self, s: &str) -> core::fmt::Result {
184 let bytes = s.as_bytes();
185 if self.pos + bytes.len() > self.buffer.len() {
186 return Err(core::fmt::Error);
187 }
188 self.buffer[self.pos..self.pos + bytes.len()].copy_from_slice(bytes);
189 self.pos += bytes.len();
190 Ok(())
191 }
192 }
193}