anomaly_grid/
error.rs

1//! Error types for the Anomaly Grid library
2//!
3//! This module provides structured error handling with detailed context
4//! and actionable error messages for all library operations.
5
6/// Custom error types for Anomaly Grid operations
7#[derive(Debug, Clone, PartialEq)]
8pub enum AnomalyGridError {
9    /// Sequence is too short for the requested operation
10    SequenceTooShort {
11        /// Minimum required sequence length
12        expected: usize,
13        /// Actual sequence length provided
14        actual: usize,
15        /// Context about what operation failed
16        operation: String,
17    },
18
19    /// Invalid max_order parameter
20    InvalidMaxOrder {
21        /// The invalid value provided
22        value: usize,
23        /// Additional context about valid range
24        context: String,
25    },
26
27    /// Invalid threshold parameter
28    InvalidThreshold {
29        /// The invalid threshold value
30        value: f64,
31        /// Expected range description
32        expected_range: String,
33    },
34
35    /// Memory limit exceeded during context tree building
36    MemoryLimitExceeded {
37        /// Current number of contexts
38        current: usize,
39        /// Maximum allowed contexts
40        limit: usize,
41        /// Suggested action
42        suggestion: String,
43    },
44
45    /// Context tree is empty (no training data)
46    EmptyContextTree {
47        /// Suggested action to resolve
48        suggestion: String,
49    },
50
51    /// Invalid configuration parameter
52    InvalidConfiguration {
53        /// Parameter name
54        parameter: String,
55        /// Invalid value
56        value: String,
57        /// Expected format or range
58        expected: String,
59    },
60}
61
62impl std::fmt::Display for AnomalyGridError {
63    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
64        match self {
65            AnomalyGridError::SequenceTooShort {
66                expected,
67                actual,
68                operation,
69            } => {
70                write!(
71                    f,
72                    "Sequence too short for {operation}: expected at least {expected} elements, got {actual}"
73                )
74            }
75            AnomalyGridError::InvalidMaxOrder { value, context } => {
76                write!(f, "Invalid max_order {value}: {context}")
77            }
78            AnomalyGridError::InvalidThreshold {
79                value,
80                expected_range,
81            } => {
82                write!(f, "Invalid threshold {value}: expected {expected_range}")
83            }
84            AnomalyGridError::MemoryLimitExceeded {
85                current,
86                limit,
87                suggestion,
88            } => {
89                write!(
90                    f,
91                    "Memory limit exceeded: {current} contexts created, limit is {limit}. {suggestion}"
92                )
93            }
94            AnomalyGridError::EmptyContextTree { suggestion } => {
95                write!(
96                    f,
97                    "Context tree is empty: no training data processed. {suggestion}"
98                )
99            }
100            AnomalyGridError::InvalidConfiguration {
101                parameter,
102                value,
103                expected,
104            } => {
105                write!(
106                    f,
107                    "Invalid configuration for '{parameter}': got '{value}', expected {expected}"
108                )
109            }
110        }
111    }
112}
113
114impl std::error::Error for AnomalyGridError {}
115
116/// Result type alias for Anomaly Grid operations
117pub type AnomalyGridResult<T> = std::result::Result<T, AnomalyGridError>;
118
119impl AnomalyGridError {
120    /// Create a sequence too short error with context
121    pub fn sequence_too_short(expected: usize, actual: usize, operation: &str) -> Self {
122        Self::SequenceTooShort {
123            expected,
124            actual,
125            operation: operation.to_string(),
126        }
127    }
128
129    /// Create an invalid max_order error
130    pub fn invalid_max_order(value: usize) -> Self {
131        Self::InvalidMaxOrder {
132            value,
133            context: "max_order must be greater than 0".to_string(),
134        }
135    }
136
137    /// Create an invalid threshold error
138    pub fn invalid_threshold(value: f64) -> Self {
139        Self::InvalidThreshold {
140            value,
141            expected_range: "a value between 0.0 and 1.0 (inclusive)".to_string(),
142        }
143    }
144
145    /// Create a memory limit exceeded error
146    pub fn memory_limit_exceeded(current: usize, limit: usize) -> Self {
147        Self::MemoryLimitExceeded {
148            current,
149            limit,
150            suggestion: "Consider reducing max_order, alphabet size, or increasing memory_limit"
151                .to_string(),
152        }
153    }
154
155    /// Create an empty context tree error
156    pub fn empty_context_tree() -> Self {
157        Self::EmptyContextTree {
158            suggestion: "Call train() with a valid sequence before detection".to_string(),
159        }
160    }
161
162    /// Create an invalid configuration error
163    pub fn invalid_configuration(parameter: &str, value: &str, expected: &str) -> Self {
164        Self::InvalidConfiguration {
165            parameter: parameter.to_string(),
166            value: value.to_string(),
167            expected: expected.to_string(),
168        }
169    }
170}
171
172#[cfg(test)]
173mod tests {
174    use super::*;
175
176    #[test]
177    fn test_sequence_too_short_error() {
178        let error = AnomalyGridError::sequence_too_short(5, 3, "training");
179        let message = error.to_string();
180
181        assert!(message.contains("training"));
182        assert!(message.contains("expected at least 5"));
183        assert!(message.contains("got 3"));
184    }
185
186    #[test]
187    fn test_invalid_max_order_error() {
188        let error = AnomalyGridError::invalid_max_order(0);
189        let message = error.to_string();
190
191        assert!(message.contains("Invalid max_order 0"));
192        assert!(message.contains("must be greater than 0"));
193    }
194
195    #[test]
196    fn test_invalid_threshold_error() {
197        let error = AnomalyGridError::invalid_threshold(1.5);
198        let message = error.to_string();
199
200        assert!(message.contains("Invalid threshold 1.5"));
201        assert!(message.contains("between 0.0 and 1.0"));
202    }
203
204    #[test]
205    fn test_memory_limit_exceeded_error() {
206        let error = AnomalyGridError::memory_limit_exceeded(150000, 100000);
207        let message = error.to_string();
208
209        assert!(message.contains("150000 contexts"));
210        assert!(message.contains("limit is 100000"));
211        assert!(message.contains("Consider reducing"));
212    }
213
214    #[test]
215    fn test_empty_context_tree_error() {
216        let error = AnomalyGridError::empty_context_tree();
217        let message = error.to_string();
218
219        assert!(message.contains("Context tree is empty"));
220        assert!(message.contains("Call train()"));
221    }
222
223    #[test]
224    fn test_error_equality() {
225        let error1 = AnomalyGridError::invalid_max_order(0);
226        let error2 = AnomalyGridError::invalid_max_order(0);
227        let error3 = AnomalyGridError::invalid_max_order(1);
228
229        assert_eq!(error1, error2);
230        assert_ne!(error1, error3);
231    }
232
233    #[test]
234    fn test_error_debug() {
235        let error = AnomalyGridError::sequence_too_short(5, 3, "testing");
236        let debug_str = format!("{error:?}");
237
238        assert!(debug_str.contains("SequenceTooShort"));
239        assert!(debug_str.contains("expected: 5"));
240        assert!(debug_str.contains("actual: 3"));
241    }
242}