Files
foxhunt/storage/src/error.rs
jgrusewski 6bd5b18465 🔧 Wave 33: Test Compilation Improvements - 57 errors remaining
**Progress: 1,178 → 57 test errors (95% reduction)**

## Status Summary
-  Production code: Compiles cleanly (0 errors)
- ⚠️  Test code: 57 errors remain (massive improvement)
- ⚙️  All services build successfully
- 📊 Warning count: 253 (target: <20) - AGENTS WILL FIX

## Remaining Test Errors (57 total)
### Primary Issues:
1. 23× E0308 mismatched types
2. 17× E0433 undeclared Decimal
3. 15× E0433 compliance module not found
4. 6× E0624 private method access
5. Various import and type issues

## Next Phase: Wave 33-2
Launch 10+ parallel agents to:
- Fix remaining 57 test compilation errors
- Reduce 253 warnings to <20
- Achieve 95% test coverage
- Ensure all tests pass

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-01 21:24:28 +02:00

333 lines
10 KiB
Rust

//! Error types for storage operations
use common::error::{CommonError, ErrorCategory};
use thiserror::Error;
/// Result type for storage operations
pub type StorageResult<T> = Result<T, StorageError>;
/// Storage-related errors
#[derive(Error, Debug)]
pub enum StorageError {
/// IO error during file operations
#[error("IO error: {message}")]
IoError {
/// Error message describing the IO failure
message: String,
},
/// Network error during remote storage operations
#[error("Network error: {message}")]
NetworkError {
/// Error message describing the network failure
message: String,
},
/// Authentication/authorization error
#[error("Authentication error: {message}")]
AuthError {
/// Error message describing the authentication failure
message: String,
},
/// Data not found at the specified path
#[error("Data not found at path: {path}")]
NotFound {
/// The path where data was not found
path: String,
},
/// Permission denied for storage operation
#[error("Permission denied for operation on: {path}")]
PermissionDenied {
/// The path where permission was denied
path: String,
},
/// Storage quota exceeded
#[error("Storage quota exceeded (used: {used}, limit: {limit})")]
QuotaExceeded {
/// Current storage usage in bytes
used: u64,
/// Storage quota limit in bytes
limit: u64,
},
/// Data integrity check failed
#[error("Data integrity check failed for: {path} (expected: {expected}, actual: {actual})")]
IntegrityError {
/// The path where integrity check failed
path: String,
/// Expected checksum or hash value
expected: String,
/// Actual checksum or hash value
actual: String,
},
/// Serialization/deserialization error
#[error("Serialization error: {message}")]
SerializationError {
/// Error message describing the serialization failure
message: String,
},
/// Compression/decompression error
#[error("Compression error: {message}")]
CompressionError {
/// Error message describing the compression failure
message: String,
},
/// Configuration error
#[error("Configuration error: {message}")]
ConfigError {
/// Error message describing the configuration issue
message: String,
},
/// Operation failed with detailed context
#[error("Operation '{operation}' failed on path '{path}': {source}")]
OperationFailed {
/// The operation that failed
operation: String,
/// The path where the operation failed
path: String,
/// The underlying error that caused the failure
#[source]
source: std::sync::Arc<CommonError>,
},
/// Timeout during storage operation
#[error("Operation timed out after {timeout_ms}ms")]
Timeout {
/// Timeout duration in milliseconds
timeout_ms: u64,
},
/// Rate limit exceeded
#[error("Rate limit exceeded, retry after {retry_after_ms}ms")]
RateLimited {
/// Time to wait before retrying in milliseconds
retry_after_ms: u64,
},
/// Generic storage error
#[error("Storage error: {message}")]
Generic {
/// Error message describing the generic failure
message: String,
},
/// S3-specific error
#[cfg(feature = "s3")]
#[error("S3 error: {message}")]
S3Error {
/// Error message describing the S3 failure
message: String,
},
}
impl StorageError {
/// Check if the error is retryable
pub fn is_retryable(&self) -> bool {
match self {
StorageError::NetworkError { .. } => true,
StorageError::Timeout { .. } => true,
StorageError::RateLimited { .. } => true,
StorageError::Generic { .. } => true,
#[cfg(feature = "s3")]
StorageError::S3Error { .. } => true,
_ => false,
}
}
/// Get retry delay in milliseconds for retryable errors
pub fn retry_delay_ms(&self) -> Option<u64> {
match self {
StorageError::NetworkError { .. } => Some(100),
StorageError::Timeout { .. } => Some(200),
StorageError::RateLimited { retry_after_ms } => Some(*retry_after_ms),
StorageError::Generic { .. } => Some(100),
#[cfg(feature = "s3")]
StorageError::S3Error { .. } => Some(100),
_ => None,
}
}
/// Check if error indicates a transient issue
pub fn is_transient(&self) -> bool {
matches!(
self,
StorageError::NetworkError { .. }
| StorageError::Timeout { .. }
| StorageError::RateLimited { .. }
)
}
/// Get error category for metrics and monitoring
pub fn category(&self) -> &'static str {
match self {
StorageError::IoError { .. } => "io",
StorageError::NetworkError { .. } => "network",
StorageError::AuthError { .. } => "auth",
StorageError::NotFound { .. } => "not_found",
StorageError::PermissionDenied { .. } => "permission",
StorageError::QuotaExceeded { .. } => "quota",
StorageError::IntegrityError { .. } => "integrity",
StorageError::SerializationError { .. } => "serialization",
StorageError::CompressionError { .. } => "compression",
StorageError::ConfigError { .. } => "config",
StorageError::OperationFailed { .. } => "operation_failed",
StorageError::Timeout { .. } => "timeout",
StorageError::RateLimited { .. } => "rate_limit",
StorageError::Generic { .. } => "generic",
#[cfg(feature = "s3")]
StorageError::S3Error { .. } => "s3",
}
}
/// Create a sanitized error message safe for logging (removes sensitive info)
pub fn safe_message(&self) -> String {
match self {
StorageError::AuthError { .. } => {
"Authentication error (details masked for security)".to_string()
},
_ => self.to_string(),
}
}
}
// Conversion implementations for common error types
impl From<std::io::Error> for StorageError {
fn from(err: std::io::Error) -> Self {
use std::io::ErrorKind;
match err.kind() {
ErrorKind::NotFound => StorageError::NotFound {
path: "unknown".to_string(),
},
ErrorKind::PermissionDenied => StorageError::PermissionDenied {
path: "unknown".to_string(),
},
ErrorKind::TimedOut => StorageError::Timeout { timeout_ms: 5000 },
_ => StorageError::IoError {
message: err.to_string(),
},
}
}
}
impl From<serde_json::Error> for StorageError {
fn from(err: serde_json::Error) -> Self {
StorageError::SerializationError {
message: err.to_string(),
}
}
}
impl From<bincode::Error> for StorageError {
fn from(err: bincode::Error) -> Self {
StorageError::SerializationError {
message: err.to_string(),
}
}
}
// Conversion to CommonError for integration with common error system
impl From<StorageError> for CommonError {
fn from(err: StorageError) -> Self {
let category = match &err {
StorageError::IoError { .. } => ErrorCategory::System,
StorageError::NetworkError { .. } => ErrorCategory::Network,
StorageError::AuthError { .. } => ErrorCategory::Security,
StorageError::NotFound { .. } => ErrorCategory::System,
StorageError::PermissionDenied { .. } => ErrorCategory::Security,
StorageError::QuotaExceeded { .. } => ErrorCategory::System,
StorageError::IntegrityError { .. } => ErrorCategory::System,
StorageError::SerializationError { .. } => ErrorCategory::System,
StorageError::CompressionError { .. } => ErrorCategory::System,
StorageError::ConfigError { .. } => ErrorCategory::Configuration,
StorageError::OperationFailed { .. } => ErrorCategory::System,
StorageError::Timeout { .. } => ErrorCategory::System,
StorageError::RateLimited { .. } => ErrorCategory::System,
StorageError::Generic { .. } => ErrorCategory::System,
StorageError::S3Error { .. } => ErrorCategory::Network,
};
CommonError::service(category, err.to_string())
}
}
// Note: AWS SDK error conversions removed - we use object_store now
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_retryability() {
assert!(StorageError::NetworkError {
message: "test".to_string()
}
.is_retryable());
assert!(!StorageError::NotFound {
path: "test".to_string()
}
.is_retryable());
assert!(StorageError::Timeout { timeout_ms: 1000 }.is_retryable());
}
#[test]
fn test_error_categories() {
assert_eq!(
StorageError::IoError {
message: "test".to_string()
}
.category(),
"io"
);
assert_eq!(
StorageError::NetworkError {
message: "test".to_string()
}
.category(),
"network"
);
assert_eq!(
StorageError::NotFound {
path: "test".to_string()
}
.category(),
"not_found"
);
}
#[test]
fn test_error_transient() {
assert!(StorageError::NetworkError {
message: "test".to_string()
}
.is_transient());
assert!(!StorageError::ConfigError {
message: "test".to_string()
}
.is_transient());
}
#[test]
fn test_safe_message() {
let auth_error = StorageError::AuthError {
message: "sensitive auth details".to_string(),
};
let safe_msg = auth_error.safe_message();
assert!(safe_msg.contains("masked"));
assert!(!safe_msg.contains("sensitive"));
}
}