stygian_plugin/storage/
memory_idempotency_store.rs1use crate::Result;
4use crate::domain::{ExtractionResult, IdempotencyKey};
5use crate::ports::IdempotencyKeyStore;
6use async_trait::async_trait;
7use std::collections::HashMap;
8use std::sync::Arc;
9use tokio::sync::RwLock;
10
11pub struct MemoryIdempotencyStore {
25 results: Arc<RwLock<HashMap<IdempotencyKey, ExtractionResult>>>,
26}
27
28impl MemoryIdempotencyStore {
29 pub fn new() -> Self {
31 Self {
32 results: Arc::new(RwLock::new(HashMap::new())),
33 }
34 }
35}
36
37impl Default for MemoryIdempotencyStore {
38 fn default() -> Self {
39 Self::new()
40 }
41}
42
43#[async_trait]
44impl IdempotencyKeyStore for MemoryIdempotencyStore {
45 async fn store_result(&self, key: &IdempotencyKey, result: &ExtractionResult) -> Result<()> {
46 self.results.write().await.insert(*key, result.clone());
47 Ok(())
48 }
49
50 async fn get_result(&self, key: &IdempotencyKey) -> Result<Option<ExtractionResult>> {
51 let results = self.results.read().await;
52 Ok(results.get(key).cloned())
53 }
54
55 async fn delete_result(&self, key: &IdempotencyKey) -> Result<()> {
56 self.results.write().await.remove(key);
57 Ok(())
58 }
59
60 async fn clear_all(&self) -> Result<()> {
61 self.results.write().await.clear();
62 Ok(())
63 }
64}
65
66#[cfg(test)]
67mod tests {
68 use super::*;
69
70 #[tokio::test]
71 async fn test_store_and_get_result() -> std::result::Result<(), Box<dyn std::error::Error>> {
72 let store = MemoryIdempotencyStore::new();
73 let key = IdempotencyKey::new();
74 let result = ExtractionResult::new(key);
75
76 store.store_result(&key, &result).await?;
77 let retrieved = store.get_result(&key).await?;
78
79 assert!(retrieved.is_some());
80 let retrieved = retrieved.ok_or("expected Some result")?;
81 assert_eq!(retrieved.metadata.idempotency_key, key);
82 Ok(())
83 }
84
85 #[tokio::test]
86 async fn test_get_nonexistent_result() -> std::result::Result<(), Box<dyn std::error::Error>> {
87 let store = MemoryIdempotencyStore::new();
88 let key = IdempotencyKey::new();
89
90 let result = store.get_result(&key).await?;
91 assert!(result.is_none());
92 Ok(())
93 }
94
95 #[tokio::test]
96 async fn test_delete_result() -> std::result::Result<(), Box<dyn std::error::Error>> {
97 let store = MemoryIdempotencyStore::new();
98 let key = IdempotencyKey::new();
99 let result = ExtractionResult::new(key);
100
101 store.store_result(&key, &result).await?;
102 store.delete_result(&key).await?;
103
104 let retrieved = store.get_result(&key).await?;
105 assert!(retrieved.is_none());
106 Ok(())
107 }
108
109 #[tokio::test]
110 async fn test_clear_all() -> std::result::Result<(), Box<dyn std::error::Error>> {
111 let store = MemoryIdempotencyStore::new();
112
113 let key1 = IdempotencyKey::new();
114 let key2 = IdempotencyKey::new();
115
116 store
117 .store_result(&key1, &ExtractionResult::new(key1))
118 .await?;
119 store
120 .store_result(&key2, &ExtractionResult::new(key2))
121 .await?;
122
123 store.clear_all().await?;
124
125 assert!(store.get_result(&key1).await?.is_none());
126 assert!(store.get_result(&key2).await?.is_none());
127 Ok(())
128 }
129}