Skip to main content

libspot_rs/
error.rs

1//! Error types for the SPOT algorithm implementation
2//!
3//! This module defines error types that match the C implementation exactly.
4
5use std::fmt;
6
7/// Result type for SPOT operations
8pub type SpotResult<T> = Result<T, SpotError>;
9
10/// Error codes that match the C implementation
11///
12/// # Serialization
13///
14/// When the `serde` feature is enabled, this enum can be serialized and deserialized.
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
17pub enum SpotError {
18    /// Memory allocation failed
19    MemoryAllocationFailed = 1000,
20    /// The level parameter must be between 0 and 1
21    LevelOutOfBounds = 1001,
22    /// The q parameter must be between 0 and 1-level
23    QOutOfBounds = 1002,
24    /// The excess threshold has not been initialized
25    ExcessThresholdIsNaN = 1003,
26    /// The anomaly threshold has not been initialized
27    AnomalyThresholdIsNaN = 1004,
28    /// The input data is NaN
29    DataIsNaN = 1005,
30}
31
32impl SpotError {
33    /// Convert from C error code
34    pub fn from_code(code: i32) -> Self {
35        match code.abs() {
36            1000 => SpotError::MemoryAllocationFailed,
37            1001 => SpotError::LevelOutOfBounds,
38            1002 => SpotError::QOutOfBounds,
39            1003 => SpotError::ExcessThresholdIsNaN,
40            1004 => SpotError::AnomalyThresholdIsNaN,
41            1005 => SpotError::DataIsNaN,
42            _ => SpotError::MemoryAllocationFailed, // Default fallback
43        }
44    }
45
46    /// Get error message
47    pub fn message(&self) -> &'static str {
48        match self {
49            SpotError::MemoryAllocationFailed => "Memory allocation failed",
50            SpotError::LevelOutOfBounds => {
51                "The level parameter is out of bounds (it must be between 0 and 1, but close to 1)"
52            }
53            SpotError::QOutOfBounds => "The q parameter must between 0 and 1-level",
54            SpotError::ExcessThresholdIsNaN => "The excess threshold has not been initialized",
55            SpotError::AnomalyThresholdIsNaN => "The anomaly threshold has not been initialized",
56            SpotError::DataIsNaN => "The input data is NaN",
57        }
58    }
59
60    /// Get error code
61    pub fn code(&self) -> i32 {
62        *self as i32
63    }
64}
65
66impl fmt::Display for SpotError {
67    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68        write!(f, "{}", self.message())
69    }
70}
71
72impl std::error::Error for SpotError {}
73
74#[cfg(test)]
75mod tests {
76    use super::*;
77
78    #[test]
79    fn test_error_codes_match_c() {
80        assert_eq!(SpotError::MemoryAllocationFailed.code(), 1000);
81        assert_eq!(SpotError::LevelOutOfBounds.code(), 1001);
82        assert_eq!(SpotError::QOutOfBounds.code(), 1002);
83        assert_eq!(SpotError::ExcessThresholdIsNaN.code(), 1003);
84        assert_eq!(SpotError::AnomalyThresholdIsNaN.code(), 1004);
85        assert_eq!(SpotError::DataIsNaN.code(), 1005);
86    }
87
88    #[test]
89    fn test_from_code() {
90        assert_eq!(
91            SpotError::from_code(-1000),
92            SpotError::MemoryAllocationFailed
93        );
94        assert_eq!(SpotError::from_code(-1001), SpotError::LevelOutOfBounds);
95        assert_eq!(SpotError::from_code(-1002), SpotError::QOutOfBounds);
96        assert_eq!(SpotError::from_code(-1003), SpotError::ExcessThresholdIsNaN);
97        assert_eq!(
98            SpotError::from_code(-1004),
99            SpotError::AnomalyThresholdIsNaN
100        );
101        assert_eq!(SpotError::from_code(-1005), SpotError::DataIsNaN);
102    }
103
104    #[test]
105    fn test_error_messages() {
106        assert_eq!(
107            SpotError::MemoryAllocationFailed.message(),
108            "Memory allocation failed"
109        );
110        assert_eq!(
111            SpotError::LevelOutOfBounds.message(),
112            "The level parameter is out of bounds (it must be between 0 and 1, but close to 1)"
113        );
114    }
115
116    #[test]
117    fn test_error_display() {
118        let error = SpotError::DataIsNaN;
119        assert_eq!(format!("{}", error), "The input data is NaN");
120    }
121}