Skip to main content

aura_core/effects/
storage.rs

1//! Storage effects for key-value operations
2//!
3//! # Effect Classification
4//!
5//! - **Category**: Infrastructure Effect
6//! - **Implementation**: `aura-effects` (Layer 3)
7//! - **Usage**: All crates needing file I/O and persistent storage operations
8//!
9//! This is an infrastructure effect that must be implemented in `aura-effects`
10//! with stateless handlers. Domain crates should not implement this trait directly.
11
12use async_trait::async_trait;
13use std::collections::HashMap;
14use std::path::{Path, PathBuf};
15
16/// Storage location wrapper (kept for backwards compatibility)
17#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
18pub struct StorageLocation {
19    path: PathBuf,
20}
21
22impl StorageLocation {
23    /// Create a new storage location
24    pub fn new(path: impl Into<PathBuf>) -> Self {
25        Self { path: path.into() }
26    }
27
28    /// Create from path
29    pub fn from_path(path: impl Into<PathBuf>) -> Self {
30        Self::new(path)
31    }
32
33    /// Get the path
34    pub fn path(&self) -> &Path {
35        &self.path
36    }
37
38    /// Get path as string
39    pub fn as_str(&self) -> &str {
40        self.path.to_str().unwrap_or("")
41    }
42}
43
44/// Storage operation errors
45#[derive(Debug, thiserror::Error, serde::Serialize, serde::Deserialize)]
46pub enum StorageError {
47    /// Failed to read data
48    #[error("Failed to read: {0}")]
49    ReadFailed(String),
50    /// Failed to write data
51    #[error("Failed to write: {0}")]
52    WriteFailed(String),
53    /// Failed to delete data
54    #[error("Failed to delete: {0}")]
55    DeleteFailed(String),
56    /// Failed to list keys
57    #[error("Failed to list: {0}")]
58    ListFailed(String),
59    /// Key not found
60    #[error("Key not found: {0}")]
61    NotFound(String),
62    /// Permission denied for storage operation
63    #[error("Permission denied: {0}")]
64    PermissionDenied(String),
65    /// Encryption failed
66    #[error("Encryption failed: {reason}")]
67    EncryptionFailed {
68        /// Reason for encryption failure
69        reason: String,
70    },
71    /// Decryption failed
72    #[error("Decryption failed: {reason}")]
73    DecryptionFailed {
74        /// Reason for decryption failure
75        reason: String,
76    },
77    /// Integrity check failed
78    #[error("Integrity check failed for key {key}: expected {expected}, got {actual}")]
79    IntegrityCheckFailed {
80        /// The key that failed integrity check
81        key: String,
82        /// Expected integrity hash
83        expected: String,
84        /// Actual integrity hash
85        actual: String,
86    },
87    /// Invalid key format
88    #[error("Invalid key: {reason}")]
89    InvalidKey {
90        /// Reason why the key format is invalid
91        reason: String,
92    },
93    /// Storage space exhausted
94    #[error("Storage space exhausted: {available} available, {required} required")]
95    SpaceExhausted {
96        /// Available storage space in bytes
97        available: u64,
98        /// Required storage space in bytes
99        required: u64,
100    },
101    /// Configuration error
102    #[error("Configuration error: {reason}")]
103    ConfigurationError {
104        /// Reason for configuration error
105        reason: String,
106    },
107    /// Data corruption detected
108    #[error("Data corruption detected: {details}")]
109    CorruptionDetected {
110        /// Details about the detected corruption
111        details: String,
112    },
113}
114
115/// Storage statistics
116#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
117pub struct StorageStats {
118    /// Number of keys stored
119    pub key_count: u64,
120    /// Total size of stored data in bytes
121    pub total_size: u64,
122    /// Available space in bytes (if known)
123    pub available_space: Option<u64>,
124    /// Backend type (e.g., "memory", "filesystem", "distributed")
125    pub backend_type: String,
126}
127
128/// Core storage effects interface for key-value operations.
129///
130/// This trait provides storage operations for the Aura effects system.
131/// Implementations in aura-protocol provide:
132/// - Production: Filesystem-based persistent storage
133/// - Testing: In-memory storage for fast tests
134/// - Simulation: Configurable storage with fault injection
135#[async_trait]
136pub trait StorageCoreEffects: Send + Sync {
137    /// Store a value under the given key
138    async fn store(&self, key: &str, value: Vec<u8>) -> Result<(), StorageError>;
139
140    /// Retrieve a value by key
141    async fn retrieve(&self, key: &str) -> Result<Option<Vec<u8>>, StorageError>;
142
143    /// Remove a key-value pair
144    async fn remove(&self, key: &str) -> Result<bool, StorageError>;
145
146    /// List all keys with optional prefix filter
147    async fn list_keys(&self, prefix: Option<&str>) -> Result<Vec<String>, StorageError>;
148}
149
150/// Optional storage effects that build on core storage.
151#[async_trait]
152pub trait StorageExtendedEffects: StorageCoreEffects + Send + Sync {
153    /// Check if a key exists
154    async fn exists(&self, key: &str) -> Result<bool, StorageError> {
155        let _ = key;
156        Err(StorageError::ConfigurationError {
157            reason: "Storage exists() not supported".to_string(),
158        })
159    }
160
161    /// Store multiple key-value pairs atomically
162    async fn store_batch(&self, _pairs: HashMap<String, Vec<u8>>) -> Result<(), StorageError> {
163        Err(StorageError::ConfigurationError {
164            reason: "Storage store_batch() not supported".to_string(),
165        })
166    }
167
168    /// Retrieve multiple values by keys
169    async fn retrieve_batch(
170        &self,
171        _keys: &[String],
172    ) -> Result<HashMap<String, Vec<u8>>, StorageError> {
173        Err(StorageError::ConfigurationError {
174            reason: "Storage retrieve_batch() not supported".to_string(),
175        })
176    }
177
178    /// Clear all stored data
179    async fn clear_all(&self) -> Result<(), StorageError> {
180        Err(StorageError::ConfigurationError {
181            reason: "Storage clear_all() not supported".to_string(),
182        })
183    }
184
185    /// Get storage statistics
186    async fn stats(&self) -> Result<StorageStats, StorageError> {
187        Err(StorageError::ConfigurationError {
188            reason: "Storage stats() not supported".to_string(),
189        })
190    }
191}
192
193/// Combined storage effects surface (core + extended).
194pub trait StorageEffects: StorageCoreEffects + StorageExtendedEffects {}
195
196impl<T: StorageCoreEffects + StorageExtendedEffects + ?Sized> StorageEffects for T {}
197
198/// Blanket implementation for Arc<T> where T: StorageCoreEffects
199#[async_trait]
200impl<T: StorageCoreEffects + ?Sized> StorageCoreEffects for std::sync::Arc<T> {
201    async fn store(&self, key: &str, value: Vec<u8>) -> Result<(), StorageError> {
202        (**self).store(key, value).await
203    }
204
205    async fn retrieve(&self, key: &str) -> Result<Option<Vec<u8>>, StorageError> {
206        (**self).retrieve(key).await
207    }
208
209    async fn remove(&self, key: &str) -> Result<bool, StorageError> {
210        (**self).remove(key).await
211    }
212
213    async fn list_keys(&self, prefix: Option<&str>) -> Result<Vec<String>, StorageError> {
214        (**self).list_keys(prefix).await
215    }
216}
217
218/// Blanket implementation for Arc<T> where T: StorageExtendedEffects
219#[async_trait]
220impl<T: StorageExtendedEffects + ?Sized> StorageExtendedEffects for std::sync::Arc<T> {
221    async fn exists(&self, key: &str) -> Result<bool, StorageError> {
222        (**self).exists(key).await
223    }
224
225    async fn store_batch(&self, pairs: HashMap<String, Vec<u8>>) -> Result<(), StorageError> {
226        (**self).store_batch(pairs).await
227    }
228
229    async fn retrieve_batch(
230        &self,
231        keys: &[String],
232    ) -> Result<HashMap<String, Vec<u8>>, StorageError> {
233        (**self).retrieve_batch(keys).await
234    }
235
236    async fn clear_all(&self) -> Result<(), StorageError> {
237        (**self).clear_all().await
238    }
239
240    async fn stats(&self) -> Result<StorageStats, StorageError> {
241        (**self).stats().await
242    }
243}