llm_sentinel_core/
error.rs

1//! Error types for Sentinel operations.
2//!
3//! This module provides a comprehensive error hierarchy for all Sentinel operations,
4//! with proper error context and conversion support.
5
6use std::fmt;
7
8/// Result type alias for Sentinel operations
9pub type Result<T> = std::result::Result<T, Error>;
10
11/// Main error type for Sentinel operations
12#[derive(Debug, thiserror::Error)]
13pub enum Error {
14    /// Configuration errors
15    #[error("Configuration error: {0}")]
16    Config(String),
17
18    /// Serialization/deserialization errors
19    #[error("Serialization error: {0}")]
20    Serialization(#[from] serde_json::Error),
21
22    /// Validation errors
23    #[error("Validation error: {0}")]
24    Validation(String),
25
26    /// IO errors
27    #[error("IO error: {0}")]
28    Io(#[from] std::io::Error),
29
30    /// Network/connection errors
31    #[error("Connection error: {0}")]
32    Connection(String),
33
34    /// Database/storage errors
35    #[error("Storage error: {0}")]
36    Storage(String),
37
38    /// Ingestion errors
39    #[error("Ingestion error: {0}")]
40    Ingestion(String),
41
42    /// Detection errors
43    #[error("Detection error: {0}")]
44    Detection(String),
45
46    /// Alerting errors
47    #[error("Alerting error: {0}")]
48    Alerting(String),
49
50    /// Internal errors
51    #[error("Internal error: {0}")]
52    Internal(String),
53
54    /// Not found errors
55    #[error("{0} not found")]
56    NotFound(String),
57
58    /// Already exists errors
59    #[error("{0} already exists")]
60    AlreadyExists(String),
61
62    /// Timeout errors
63    #[error("Operation timed out: {0}")]
64    Timeout(String),
65
66    /// Rate limit errors
67    #[error("Rate limit exceeded: {0}")]
68    RateLimit(String),
69
70    /// Generic errors with context
71    #[error("{context}: {source}")]
72    WithContext {
73        /// Error context
74        context: String,
75        /// Source error
76        source: Box<Error>,
77    },
78}
79
80impl Error {
81    /// Add context to an error
82    pub fn context<C: fmt::Display>(self, context: C) -> Self {
83        Error::WithContext {
84            context: context.to_string(),
85            source: Box::new(self),
86        }
87    }
88
89    /// Create a configuration error
90    pub fn config<S: Into<String>>(msg: S) -> Self {
91        Error::Config(msg.into())
92    }
93
94    /// Create a validation error
95    pub fn validation<S: Into<String>>(msg: S) -> Self {
96        Error::Validation(msg.into())
97    }
98
99    /// Create a connection error
100    pub fn connection<S: Into<String>>(msg: S) -> Self {
101        Error::Connection(msg.into())
102    }
103
104    /// Create a storage error
105    pub fn storage<S: Into<String>>(msg: S) -> Self {
106        Error::Storage(msg.into())
107    }
108
109    /// Create an ingestion error
110    pub fn ingestion<S: Into<String>>(msg: S) -> Self {
111        Error::Ingestion(msg.into())
112    }
113
114    /// Create a detection error
115    pub fn detection<S: Into<String>>(msg: S) -> Self {
116        Error::Detection(msg.into())
117    }
118
119    /// Create an alerting error
120    pub fn alerting<S: Into<String>>(msg: S) -> Self {
121        Error::Alerting(msg.into())
122    }
123
124    /// Create an internal error
125    pub fn internal<S: Into<String>>(msg: S) -> Self {
126        Error::Internal(msg.into())
127    }
128
129    /// Create a not found error
130    pub fn not_found<S: Into<String>>(item: S) -> Self {
131        Error::NotFound(item.into())
132    }
133
134    /// Create an already exists error
135    pub fn already_exists<S: Into<String>>(item: S) -> Self {
136        Error::AlreadyExists(item.into())
137    }
138
139    /// Create a timeout error
140    pub fn timeout<S: Into<String>>(msg: S) -> Self {
141        Error::Timeout(msg.into())
142    }
143
144    /// Create a rate limit error
145    pub fn rate_limit<S: Into<String>>(msg: S) -> Self {
146        Error::RateLimit(msg.into())
147    }
148
149    /// Check if error is retryable
150    pub fn is_retryable(&self) -> bool {
151        matches!(
152            self,
153            Error::Connection(_) | Error::Timeout(_) | Error::Storage(_)
154        )
155    }
156
157    /// Check if error is transient
158    pub fn is_transient(&self) -> bool {
159        matches!(
160            self,
161            Error::Connection(_) | Error::Timeout(_) | Error::RateLimit(_)
162        )
163    }
164}
165
166#[cfg(test)]
167mod tests {
168    use super::*;
169
170    #[test]
171    fn test_error_creation() {
172        let err = Error::config("test error");
173        assert!(matches!(err, Error::Config(_)));
174    }
175
176    #[test]
177    fn test_error_context() {
178        let err = Error::storage("database error").context("Failed to save event");
179        assert!(matches!(err, Error::WithContext { .. }));
180    }
181
182    #[test]
183    fn test_error_retryable() {
184        assert!(Error::connection("test").is_retryable());
185        assert!(Error::timeout("test").is_retryable());
186        assert!(!Error::validation("test").is_retryable());
187    }
188
189    #[test]
190    fn test_error_transient() {
191        assert!(Error::connection("test").is_transient());
192        assert!(Error::rate_limit("test").is_transient());
193        assert!(!Error::validation("test").is_transient());
194    }
195}