guardian_db/stores/event_log_store/
index.rs

1use crate::error::GuardianError;
2use crate::ipfs_log::{entry::Entry, log::Log};
3use crate::traits::StoreIndex;
4use parking_lot::RwLock;
5use std::sync::Arc;
6
7/// `EventIndex` armazena uma cópia do log completo para queries e stream de eventos.
8///
9/// Um EventLogStore é um log de eventos "append-only" onde todas as operações
10/// são do tipo "ADD" e o índice mantém acesso ao log completo para permitir
11/// queries temporais e streaming de eventos.
12pub struct EventIndex {
13    /// Cache de entradas para acesso rápido por posição
14    entries_cache: Arc<RwLock<Vec<Entry>>>,
15}
16
17impl Default for EventIndex {
18    fn default() -> Self {
19        Self::new()
20    }
21}
22
23impl EventIndex {
24    /// Construtor padrão para um EventIndex.
25    pub fn new() -> Self {
26        EventIndex {
27            entries_cache: Arc::new(RwLock::new(Vec::new())),
28        }
29    }
30
31    /// Retorna o número de entradas no log
32    pub fn len(&self) -> usize {
33        let cache = self.entries_cache.read();
34        cache.len()
35    }
36
37    /// Verifica se o log está vazio
38    pub fn is_empty(&self) -> bool {
39        let cache = self.entries_cache.read();
40        cache.is_empty()
41    }
42
43    /// Obtém todas as entradas do log
44    pub fn get_all_entries(&self) -> Vec<Entry> {
45        let cache = self.entries_cache.read();
46        cache.clone()
47    }
48
49    /// Obtém uma entrada específica por índice
50    pub fn get_entry_at(&self, index: usize) -> Option<Entry> {
51        let cache = self.entries_cache.read();
52        cache.get(index).cloned()
53    }
54
55    /// Obtém as últimas N entradas
56    pub fn get_last_entries(&self, count: usize) -> Vec<Entry> {
57        let cache = self.entries_cache.read();
58        let start = cache.len().saturating_sub(count);
59        cache[start..].to_vec()
60    }
61}
62
63/// Implementação do trait StoreIndex para EventIndex.
64impl StoreIndex for EventIndex {
65    type Error = GuardianError;
66
67    /// Verifica se uma chave existe no índice.
68    /// Para EventLogStore, a chave é interpretada como um índice numérico.
69    fn contains_key(&self, key: &str) -> std::result::Result<bool, Self::Error> {
70        if let Ok(index) = key.parse::<usize>() {
71            let cache = self.entries_cache.read();
72            Ok(index < cache.len())
73        } else {
74            Ok(false)
75        }
76    }
77
78    /// Retorna uma entrada específica como bytes.
79    /// Para EventLogStore, retorna o payload da entrada no índice especificado.
80    fn get_bytes(&self, key: &str) -> std::result::Result<Option<Vec<u8>>, Self::Error> {
81        if let Ok(index) = key.parse::<usize>() {
82            let cache = self.entries_cache.read();
83            if let Some(entry) = cache.get(index) {
84                // Retorna o payload da entrada como bytes
85                Ok(Some(entry.payload().as_bytes().to_vec()))
86            } else {
87                Ok(None)
88            }
89        } else {
90            Ok(None)
91        }
92    }
93
94    /// Retorna todas as "chaves" disponíveis (índices) como strings.
95    fn keys(&self) -> std::result::Result<Vec<String>, GuardianError> {
96        let cache = self.entries_cache.read();
97
98        // Return indices as string keys
99        let keys: Vec<String> = (0..cache.len()).map(|i| i.to_string()).collect();
100
101        Ok(keys)
102    }
103
104    /// Retorna o número de entradas no log.
105    fn len(&self) -> std::result::Result<usize, Self::Error> {
106        let cache = self.entries_cache.read();
107        Ok(cache.len())
108    }
109
110    /// Verifica se o log está vazio.
111    fn is_empty(&self) -> std::result::Result<bool, Self::Error> {
112        let cache = self.entries_cache.read();
113        Ok(cache.is_empty())
114    }
115
116    /// Substitui o índice interno pelo novo log fornecido e atualiza o cache.
117    /// Como Log não implementa Clone, vamos reconstruir o cache
118    /// diretamente das entradas fornecidas, que é mais eficiente.
119    fn update_index(
120        &mut self,
121        _log: &Log,
122        entries: &[Entry],
123    ) -> std::result::Result<(), Self::Error> {
124        // Atualiza o cache diretamente com as entradas fornecidas
125        {
126            let mut cache = self.entries_cache.write();
127            cache.clear();
128            cache.extend_from_slice(entries);
129        }
130
131        Ok(())
132    }
133
134    /// Limpa todas as entradas do log.
135    fn clear(&mut self) -> std::result::Result<(), Self::Error> {
136        let mut cache = self.entries_cache.write();
137        cache.clear();
138        Ok(())
139    }
140
141    // === IMPLEMENTAÇÃO DOS MÉTODOS OPCIONAIS DE OTIMIZAÇÃO ===
142
143    /// Implementa acesso otimizado a range de entradas para EventLogStore.
144    ///
145    /// EventIndex mantém Entry completas em cache, permitindo acesso
146    /// direto sem necessidade de deserialização.
147    fn get_entries_range(&self, start: usize, end: usize) -> Option<Vec<Entry>> {
148        let cache = self.entries_cache.read();
149
150        // Validação de bounds
151        if start > end || start >= cache.len() {
152            return None;
153        }
154
155        let actual_end = end.min(cache.len());
156        Some(cache[start..actual_end].to_vec())
157    }
158
159    /// Acesso otimizado às últimas N entradas.
160    ///
161    /// Caso de uso muito comum para EventLogStore - buscar eventos recentes.
162    fn get_last_entries(&self, count: usize) -> Option<Vec<Entry>> {
163        let cache = self.entries_cache.read();
164
165        if cache.is_empty() || count == 0 {
166            return Some(Vec::new());
167        }
168
169        let start = cache.len().saturating_sub(count);
170        Some(cache[start..].to_vec())
171    }
172
173    /// Busca otimizada por CID.
174    ///
175    /// Atualmente usa busca linear O(n), mas estrutura preparada
176    /// para futuro índice secundário O(1) por CID.
177    fn get_entry_by_cid(&self, cid: &cid::Cid) -> Option<Entry> {
178        let cache = self.entries_cache.read();
179        let cid_str = cid.to_string();
180
181        // Busca linear por enquanto - futuro: HashMap<CID, Entry>
182        cache.iter().find(|entry| entry.hash() == cid_str).cloned()
183    }
184
185    /// EventIndex suporta queries otimizadas com Entry completas.
186    fn supports_entry_queries(&self) -> bool {
187        true
188    }
189}
190
191/// Esta é a função fábrica que cria uma nova instância do índice.
192pub fn new_event_index(_params: &[u8]) -> Box<dyn StoreIndex<Error = GuardianError>> {
193    Box::new(EventIndex::new())
194}
195
196#[cfg(test)]
197mod tests {
198    use super::*;
199    use crate::ipfs_log::{
200        entry::Entry,
201        identity::{Identity, Signatures},
202    };
203    use std::sync::Arc;
204
205    fn create_test_identity() -> Arc<Identity> {
206        // Create a simple test identity
207        Arc::new(Identity::new(
208            "test_id",
209            "test_public_key",
210            Signatures::new("id_signature", "public_signature"),
211        ))
212    }
213
214    fn create_test_entry(payload: &str) -> Entry {
215        let identity = (*create_test_identity()).clone();
216
217        // Create a simple test entry using the correct signature
218        Entry::new(
219            identity,
220            "test_log", // log_id
221            payload,    // data
222            &[],        // next (EntryOrHash slice)
223            None,       // clock
224        )
225    }
226
227    #[test]
228    fn test_event_index_creation() {
229        let index = EventIndex::new();
230        assert!(index.is_empty());
231        assert_eq!(index.len(), 0);
232    }
233
234    #[test]
235    fn test_event_index_basic_operations() {
236        let index = EventIndex::new();
237
238        // Test initial state
239        assert!(index.is_empty());
240        assert_eq!(index.len(), 0);
241        assert!(index.get_all_entries().is_empty());
242        assert!(index.get_entry_at(0).is_none());
243        assert!(index.get_last_entries(5).is_empty());
244    }
245
246    #[test]
247    fn test_entries_cache_functionality() {
248        let index = EventIndex::new();
249
250        // Simula dados no cache diretamente (para teste de cache)
251        {
252            let mut cache = index.entries_cache.write();
253            cache.push(create_test_entry("test1"));
254            cache.push(create_test_entry("test2"));
255            cache.push(create_test_entry("test3"));
256        }
257
258        assert_eq!(index.len(), 3);
259        assert!(!index.is_empty());
260
261        let all_entries = index.get_all_entries();
262        assert_eq!(all_entries.len(), 3);
263
264        let entry_at_1 = index.get_entry_at(1);
265        assert!(entry_at_1.is_some());
266        assert_eq!(entry_at_1.unwrap().payload(), "test2");
267
268        let last_2 = index.get_last_entries(2);
269        assert_eq!(last_2.len(), 2);
270        assert_eq!(last_2[0].payload(), "test2");
271        assert_eq!(last_2[1].payload(), "test3");
272    }
273
274    #[test]
275    fn test_new_event_index_factory() {
276        let params = b"test_params";
277        let index_box = new_event_index(params);
278
279        // Verify it returns a valid StoreIndex with the new interface
280        assert!(index_box.is_empty().unwrap()); // Should be empty initially
281        assert_eq!(index_box.len().unwrap(), 0); // Should have length 0
282    }
283
284    #[test]
285    fn test_store_index_trait_implementation() {
286        let mut index = EventIndex::new();
287
288        // Test new trait methods using the internal EventIndex methods
289        assert!(index.is_empty());
290        assert_eq!(index.len(), 0);
291        assert!(index.get_all_entries().is_empty());
292
293        // Test trait methods
294        assert!(
295            (&index as &dyn StoreIndex<Error = GuardianError>)
296                .is_empty()
297                .unwrap()
298        );
299        assert_eq!(
300            (&index as &dyn StoreIndex<Error = GuardianError>)
301                .len()
302                .unwrap(),
303            0
304        );
305        assert!(
306            (&index as &dyn StoreIndex<Error = GuardianError>)
307                .keys()
308                .unwrap()
309                .is_empty()
310        );
311        assert!(
312            !(&index as &dyn StoreIndex<Error = GuardianError>)
313                .contains_key("0")
314                .unwrap()
315        );
316        assert!(
317            (&index as &dyn StoreIndex<Error = GuardianError>)
318                .get_bytes("0")
319                .unwrap()
320                .is_none()
321        );
322
323        // Test after adding some entries to cache
324        {
325            let mut cache = index.entries_cache.write();
326            cache.push(create_test_entry("test1"));
327            cache.push(create_test_entry("test2"));
328        }
329
330        // Test with data using trait methods
331        let store_index = &index as &dyn StoreIndex<Error = GuardianError>;
332        assert!(!store_index.is_empty().unwrap());
333        assert_eq!(store_index.len().unwrap(), 2);
334        assert_eq!(store_index.keys().unwrap(), vec!["0", "1"]);
335        assert!(store_index.contains_key("0").unwrap());
336        assert!(store_index.contains_key("1").unwrap());
337        assert!(!store_index.contains_key("2").unwrap());
338
339        // Test get_bytes
340        let bytes_0 = store_index.get_bytes("0").unwrap();
341        assert!(bytes_0.is_some());
342        assert_eq!(bytes_0.unwrap(), b"test1".to_vec());
343
344        let bytes_1 = store_index.get_bytes("1").unwrap();
345        assert!(bytes_1.is_some());
346        assert_eq!(bytes_1.unwrap(), b"test2".to_vec());
347
348        // Test clear
349        index.clear().unwrap();
350        assert!(index.is_empty());
351        assert_eq!(index.len(), 0);
352    }
353}