ruv_swarm_persistence/
lib.rs

1//! Persistence layer for RUV-Swarm with SQLite and ORM support
2//!
3//! This crate provides a flexible persistence layer with support for:
4//! - SQLite backend for native platforms
5//! - IndexedDB backend for WASM targets
6//! - In-memory storage for testing
7//! - Repository pattern with type-safe queries
8//! - Transaction support and connection pooling
9
10pub mod memory;
11pub mod migrations;
12pub mod models;
13#[cfg(not(target_arch = "wasm32"))]
14pub mod sqlite;
15#[cfg(target_arch = "wasm32")]
16pub mod wasm;
17
18use async_trait::async_trait;
19use std::error::Error as StdError;
20use thiserror::Error;
21
22pub use models::{AgentModel, EventModel, MessageModel, MetricModel, TaskModel};
23
24#[cfg(not(target_arch = "wasm32"))]
25pub use sqlite::SqliteStorage;
26#[cfg(target_arch = "wasm32")]
27pub use wasm::WasmStorage;
28pub use memory::MemoryStorage;
29
30/// Storage error types
31#[derive(Error, Debug)]
32pub enum StorageError {
33    #[error("Database error: {0}")]
34    Database(String),
35    
36    #[error("Serialization error: {0}")]
37    Serialization(#[from] serde_json::Error),
38    
39    #[error("Not found: {0}")]
40    NotFound(String),
41    
42    #[error("Transaction error: {0}")]
43    Transaction(String),
44    
45    #[error("Migration error: {0}")]
46    Migration(String),
47    
48    #[error("Connection pool error: {0}")]
49    Pool(String),
50    
51    #[error("Other error: {0}")]
52    Other(String),
53}
54
55impl From<Box<dyn StdError + Send + Sync>> for StorageError {
56    fn from(err: Box<dyn StdError + Send + Sync>) -> Self {
57        StorageError::Other(err.to_string())
58    }
59}
60
61/// Storage trait for persistence operations
62#[async_trait]
63pub trait Storage: Send + Sync {
64    type Error: StdError + Send + Sync + 'static;
65    
66    // Agent operations
67    async fn store_agent(&self, agent: &AgentModel) -> Result<(), Self::Error>;
68    async fn get_agent(&self, id: &str) -> Result<Option<AgentModel>, Self::Error>;
69    async fn update_agent(&self, agent: &AgentModel) -> Result<(), Self::Error>;
70    async fn delete_agent(&self, id: &str) -> Result<(), Self::Error>;
71    async fn list_agents(&self) -> Result<Vec<AgentModel>, Self::Error>;
72    async fn list_agents_by_status(&self, status: &str) -> Result<Vec<AgentModel>, Self::Error>;
73    
74    // Task operations
75    async fn store_task(&self, task: &TaskModel) -> Result<(), Self::Error>;
76    async fn get_task(&self, id: &str) -> Result<Option<TaskModel>, Self::Error>;
77    async fn update_task(&self, task: &TaskModel) -> Result<(), Self::Error>;
78    async fn get_pending_tasks(&self) -> Result<Vec<TaskModel>, Self::Error>;
79    async fn get_tasks_by_agent(&self, agent_id: &str) -> Result<Vec<TaskModel>, Self::Error>;
80    async fn claim_task(&self, task_id: &str, agent_id: &str) -> Result<bool, Self::Error>;
81    
82    // Event operations
83    async fn store_event(&self, event: &EventModel) -> Result<(), Self::Error>;
84    async fn get_events_by_agent(&self, agent_id: &str, limit: usize) -> Result<Vec<EventModel>, Self::Error>;
85    async fn get_events_by_type(&self, event_type: &str, limit: usize) -> Result<Vec<EventModel>, Self::Error>;
86    async fn get_events_since(&self, timestamp: i64) -> Result<Vec<EventModel>, Self::Error>;
87    
88    // Message operations
89    async fn store_message(&self, message: &MessageModel) -> Result<(), Self::Error>;
90    async fn get_messages_between(&self, agent1: &str, agent2: &str, limit: usize) -> Result<Vec<MessageModel>, Self::Error>;
91    async fn get_unread_messages(&self, agent_id: &str) -> Result<Vec<MessageModel>, Self::Error>;
92    async fn mark_message_read(&self, message_id: &str) -> Result<(), Self::Error>;
93    
94    // Metric operations
95    async fn store_metric(&self, metric: &MetricModel) -> Result<(), Self::Error>;
96    async fn get_metrics_by_agent(&self, agent_id: &str, metric_type: &str) -> Result<Vec<MetricModel>, Self::Error>;
97    async fn get_aggregated_metrics(&self, metric_type: &str, start_time: i64, end_time: i64) -> Result<Vec<MetricModel>, Self::Error>;
98    
99    // Transaction support
100    async fn begin_transaction(&self) -> Result<Box<dyn Transaction>, Self::Error>;
101    
102    // Maintenance operations
103    async fn vacuum(&self) -> Result<(), Self::Error>;
104    async fn checkpoint(&self) -> Result<(), Self::Error>;
105    async fn get_storage_size(&self) -> Result<u64, Self::Error>;
106}
107
108/// Transaction trait for atomic operations
109#[async_trait]
110pub trait Transaction: Send + Sync {
111    async fn commit(self: Box<Self>) -> Result<(), StorageError>;
112    async fn rollback(self: Box<Self>) -> Result<(), StorageError>;
113}
114
115/// Query builder for type-safe queries
116pub struct QueryBuilder<T> {
117    table: String,
118    conditions: Vec<String>,
119    order_by: Option<String>,
120    limit: Option<usize>,
121    offset: Option<usize>,
122    _phantom: std::marker::PhantomData<T>,
123}
124
125impl<T> QueryBuilder<T> {
126    pub fn new(table: &str) -> Self {
127        Self {
128            table: table.to_string(),
129            conditions: Vec::new(),
130            order_by: None,
131            limit: None,
132            offset: None,
133            _phantom: std::marker::PhantomData,
134        }
135    }
136    
137    pub fn where_eq(mut self, field: &str, value: &str) -> Self {
138        self.conditions.push(format!("{} = '{}'", field, value));
139        self
140    }
141    
142    pub fn where_like(mut self, field: &str, pattern: &str) -> Self {
143        self.conditions.push(format!("{} LIKE '{}'", field, pattern));
144        self
145    }
146    
147    pub fn where_gt(mut self, field: &str, value: i64) -> Self {
148        self.conditions.push(format!("{} > {}", field, value));
149        self
150    }
151    
152    pub fn order_by(mut self, field: &str, desc: bool) -> Self {
153        self.order_by = Some(format!("{} {}", field, if desc { "DESC" } else { "ASC" }));
154        self
155    }
156    
157    pub fn limit(mut self, limit: usize) -> Self {
158        self.limit = Some(limit);
159        self
160    }
161    
162    pub fn offset(mut self, offset: usize) -> Self {
163        self.offset = Some(offset);
164        self
165    }
166    
167    pub fn build(&self) -> String {
168        let mut query = format!("SELECT * FROM {}", self.table);
169        
170        if !self.conditions.is_empty() {
171            query.push_str(" WHERE ");
172            query.push_str(&self.conditions.join(" AND "));
173        }
174        
175        if let Some(ref order) = self.order_by {
176            query.push_str(" ORDER BY ");
177            query.push_str(order);
178        }
179        
180        if let Some(limit) = self.limit {
181            query.push_str(&format!(" LIMIT {}", limit));
182        }
183        
184        if let Some(offset) = self.offset {
185            query.push_str(&format!(" OFFSET {}", offset));
186        }
187        
188        query
189    }
190}
191
192/// Repository pattern implementation
193pub trait Repository<T> {
194    type Error: StdError + Send + Sync + 'static;
195    
196    fn find_by_id(&self, id: &str) -> Result<Option<T>, Self::Error>;
197    fn find_all(&self) -> Result<Vec<T>, Self::Error>;
198    fn save(&self, entity: &T) -> Result<(), Self::Error>;
199    fn update(&self, entity: &T) -> Result<(), Self::Error>;
200    fn delete(&self, id: &str) -> Result<(), Self::Error>;
201    fn query(&self, builder: QueryBuilder<T>) -> Result<Vec<T>, Self::Error>;
202}
203
204/// Initialize storage based on target platform
205pub async fn init_storage(path: Option<&str>) -> Result<Box<dyn Storage<Error = StorageError>>, StorageError> {
206    #[cfg(not(target_arch = "wasm32"))]
207    {
208        let path = path.unwrap_or("swarm.db");
209        Ok(Box::new(SqliteStorage::new(path).await?))
210    }
211    
212    #[cfg(target_arch = "wasm32")]
213    {
214        Ok(Box::new(WasmStorage::new().await?))
215    }
216}
217
218#[cfg(test)]
219mod tests;