cache_kit/
error.rs

1//! Error types for the cache framework.
2
3use std::fmt;
4
5/// Result type for cache operations.
6pub type Result<T> = std::result::Result<T, Error>;
7
8/// Error types for cache framework.
9///
10/// All cache operations return `Result<T>` where `Result` is defined as `std::result::Result<T, Error>`.
11/// Different error variants represent different failure modes:
12#[derive(Debug, Clone)]
13pub enum Error {
14    /// Serialization failed when converting entity to cache bytes.
15    ///
16    /// This occurs when the entity's `Serde` implementation fails.
17    /// Common causes:
18    /// - Entity contains non-serializable types
19    /// - Serde serialization panic/error
20    /// - Postcard codec error
21    SerializationError(String),
22
23    /// Deserialization failed when converting cache bytes to entity.
24    ///
25    /// This indicates corrupted or malformed data in cache.
26    /// Common causes:
27    /// - Cache was corrupted during transport or storage
28    /// - Invalid Postcard encoding
29    /// - Incomplete data read from backend
30    ///
31    /// **Recovery:** Cache entry should be evicted and recomputed.
32    DeserializationError(String),
33
34    /// Validation failed in feeder or entity.
35    ///
36    /// This is raised when:
37    /// - `CacheFeed::validate()` returns an error
38    /// - `CacheEntity::validate()` returns an error after deserialization
39    ///
40    /// Implement these methods to add custom validation logic.
41    ValidationError(String),
42
43    /// Cache miss: key not found in cache.
44    ///
45    /// Not necessarily an error condition, but indicates cache entry was absent.
46    /// Only returned with `CacheStrategy::Fresh` when cache lookup fails.
47    CacheMiss,
48
49    /// Backend storage error (Redis, Memcached, etc).
50    ///
51    /// This indicates the cache backend is unavailable or returned an error.
52    /// Common causes:
53    /// - Redis/Memcached connection lost
54    /// - Network timeout
55    /// - Backend storage full
56    /// - Backend protocol error
57    ///
58    /// **Recovery:** Retry the operation or fallback to database.
59    BackendError(String),
60
61    /// Data repository error (database, etc).
62    ///
63    /// This indicates the source repository (database) failed to fetch data.
64    /// Common causes:
65    /// - Database connection lost
66    /// - Query syntax error
67    /// - Database server error
68    /// - Row/record not found
69    ///
70    /// **Recovery:** Retry after connection recovery.
71    RepositoryError(String),
72
73    /// Operation exceeded configured timeout threshold.
74    ///
75    /// This occurs when cache or repository operations take too long.
76    /// Common causes:
77    /// - Network latency
78    /// - Slow database query
79    /// - Backend overload
80    ///
81    /// **Recovery:** Retry with exponential backoff.
82    Timeout(String),
83
84    /// Configuration error during crate initialization.
85    ///
86    /// This occurs when creating backends or policies with invalid config.
87    /// Common causes:
88    /// - Invalid connection string
89    /// - Missing required configuration
90    /// - Invalid TTL policy
91    ///
92    /// **Recovery:** Fix configuration and restart.
93    ConfigError(String),
94
95    /// Feature not implemented or not enabled.
96    ///
97    /// This indicates a requested feature is not available.
98    /// Common causes:
99    /// - Cargo feature not enabled (e.g., "redis" for RedisBackend)
100    /// - Backend-specific operation called on wrong backend type
101    ///
102    /// **Recovery:** Enable the required Cargo feature.
103    NotImplemented(String),
104
105    /// Invalid cache entry: corrupted envelope or bad magic.
106    ///
107    /// This indicates the cache entry header is invalid.
108    /// Returned when:
109    /// - Magic header is not `b"CKIT"`
110    /// - Envelope deserialization fails
111    /// - Non-cache-kit data in cache key
112    ///
113    /// **Recovery:** Evict the cache entry and recompute.
114    InvalidCacheEntry(String),
115
116    /// Schema version mismatch between code and cached data.
117    ///
118    /// This indicates the cache entry was created with a different schema version.
119    /// Raised when:
120    /// - `CURRENT_SCHEMA_VERSION` changed
121    /// - Struct fields were added/removed/reordered
122    /// - Enum variants changed
123    ///
124    /// **Recovery:** Cache entry is automatically evicted and recomputed on next access.
125    /// No action needed - this is expected during deployments.
126    VersionMismatch {
127        /// Expected schema version (from compiled code)
128        expected: u32,
129        /// Found schema version (from cached entry)
130        found: u32,
131    },
132
133    /// Generic error with custom message.
134    ///
135    /// Used for errors that don't fit into other variants.
136    Other(String),
137}
138
139impl fmt::Display for Error {
140    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
141        match self {
142            Error::SerializationError(msg) => write!(f, "Serialization error: {}", msg),
143            Error::DeserializationError(msg) => write!(f, "Deserialization error: {}", msg),
144            Error::ValidationError(msg) => write!(f, "Validation error: {}", msg),
145            Error::CacheMiss => write!(f, "Cache miss"),
146            Error::BackendError(msg) => write!(f, "Backend error: {}", msg),
147            Error::RepositoryError(msg) => write!(f, "Repository error: {}", msg),
148            Error::Timeout(msg) => write!(f, "Timeout: {}", msg),
149            Error::ConfigError(msg) => write!(f, "Config error: {}", msg),
150            Error::NotImplemented(msg) => write!(f, "Not implemented: {}", msg),
151            Error::InvalidCacheEntry(msg) => {
152                write!(f, "Invalid cache entry: {}", msg)
153            }
154            Error::VersionMismatch { expected, found } => {
155                write!(
156                    f,
157                    "Cache version mismatch: expected {}, found {}",
158                    expected, found
159                )
160            }
161            Error::Other(msg) => write!(f, "Error: {}", msg),
162        }
163    }
164}
165
166impl std::error::Error for Error {}
167
168// ============================================================================
169// Conversions from other error types
170// ============================================================================
171
172impl From<serde_json::Error> for Error {
173    fn from(e: serde_json::Error) -> Self {
174        if e.is_io() {
175            Error::BackendError(e.to_string())
176        } else if e.is_syntax() {
177            Error::DeserializationError(e.to_string())
178        } else {
179            Error::SerializationError(e.to_string())
180        }
181    }
182}
183
184impl From<std::io::Error> for Error {
185    fn from(e: std::io::Error) -> Self {
186        Error::BackendError(e.to_string())
187    }
188}
189
190impl From<String> for Error {
191    fn from(e: String) -> Self {
192        Error::Other(e)
193    }
194}
195
196impl From<&str> for Error {
197    fn from(e: &str) -> Self {
198        Error::Other(e.to_string())
199    }
200}
201
202#[cfg(feature = "redis")]
203impl From<redis::RedisError> for Error {
204    fn from(e: redis::RedisError) -> Self {
205        Error::BackendError(format!("Redis error: {}", e))
206    }
207}
208
209#[cfg(test)]
210mod tests {
211    use super::*;
212
213    #[test]
214    fn test_error_display() {
215        let err = Error::ValidationError("Test".to_string());
216        assert_eq!(err.to_string(), "Validation error: Test");
217    }
218
219    #[test]
220    fn test_error_from_string() {
221        let err: Error = "test error".into();
222        assert!(matches!(err, Error::Other(_)));
223    }
224}