Skip to main content

uvb_storage_memory/
transaction.rs

1use async_trait::async_trait;
2use std::collections::HashMap;
3use std::sync::Arc;
4use std::time::SystemTime;
5use tokio::sync::RwLock;
6use uvb_storage_api::{TransactionError, TransactionRecord, TransactionStatus, TransactionStore};
7
8/// In-memory transaction store (for development/testing)
9pub struct InMemoryTransactionStore {
10    transactions: Arc<RwLock<HashMap<String, TransactionRecord>>>,
11}
12
13impl InMemoryTransactionStore {
14    pub fn new() -> Self {
15        Self {
16            transactions: Arc::new(RwLock::new(HashMap::new())),
17        }
18    }
19}
20
21impl Default for InMemoryTransactionStore {
22    fn default() -> Self {
23        Self::new()
24    }
25}
26
27#[async_trait]
28impl TransactionStore for InMemoryTransactionStore {
29    async fn create(&self, record: TransactionRecord) -> Result<(), TransactionError> {
30        let mut txns = self.transactions.write().await;
31        txns.insert(record.id.clone(), record);
32        Ok(())
33    }
34
35    async fn get(&self, id: &str) -> Result<Option<TransactionRecord>, TransactionError> {
36        let txns = self.transactions.read().await;
37        Ok(txns.get(id).cloned())
38    }
39
40    async fn update(&self, record: TransactionRecord) -> Result<(), TransactionError> {
41        let mut txns = self.transactions.write().await;
42        if !txns.contains_key(&record.id) {
43            return Err(TransactionError::NotFound);
44        }
45        txns.insert(record.id.clone(), record);
46        Ok(())
47    }
48
49    async fn delete(&self, id: &str) -> Result<(), TransactionError> {
50        let mut txns = self.transactions.write().await;
51        txns.remove(id).ok_or(TransactionError::NotFound)?;
52        Ok(())
53    }
54
55    async fn list_by_user(
56        &self,
57        user_id: &str,
58        limit: usize,
59    ) -> Result<Vec<TransactionRecord>, TransactionError> {
60        let txns = self.transactions.read().await;
61        let mut results: Vec<TransactionRecord> = txns
62            .values()
63            .filter(|t| t.subject.user_id == user_id)
64            .cloned()
65            .collect();
66
67        // Sort by creation time, newest first
68        results.sort_by_key(|a| std::cmp::Reverse(a.created_at));
69        results.truncate(limit);
70
71        Ok(results)
72    }
73
74    async fn cleanup_expired(&self) -> Result<usize, TransactionError> {
75        let mut txns = self.transactions.write().await;
76        let now = SystemTime::now();
77
78        let expired_ids: Vec<String> = txns
79            .iter()
80            .filter(|(_, t)| t.expires_at <= now && t.status != TransactionStatus::Succeeded)
81            .map(|(id, _)| id.clone())
82            .collect();
83
84        let count = expired_ids.len();
85        for id in expired_ids {
86            if let Some(txn) = txns.get_mut(&id) {
87                txn.status = TransactionStatus::Expired;
88            }
89        }
90
91        Ok(count)
92    }
93}