uvb-storage-memory 0.2.1

In-memory storage backend for UVB testing and development
Documentation
use async_trait::async_trait;
use std::collections::VecDeque;
use std::sync::Arc;
use tokio::sync::RwLock;
use uvb_storage_api::{AuditError, AuditEvent, AuditLogStore, AuditQueryFilters};

const MAX_EVENTS: usize = 10_000; // Prevent unbounded growth

pub struct InMemoryAuditLogStore {
    events: Arc<RwLock<VecDeque<AuditEvent>>>,
}

impl InMemoryAuditLogStore {
    pub fn new() -> Self {
        Self {
            events: Arc::new(RwLock::new(VecDeque::new())),
        }
    }
}

impl Default for InMemoryAuditLogStore {
    fn default() -> Self {
        Self::new()
    }
}

#[async_trait]
impl AuditLogStore for InMemoryAuditLogStore {
    async fn log(&self, event: AuditEvent) -> Result<(), AuditError> {
        let mut events = self.events.write().await;

        // Trim old events if we exceed max
        if events.len() >= MAX_EVENTS {
            events.pop_front();
        }

        events.push_back(event);
        Ok(())
    }

    async fn query(&self, filters: AuditQueryFilters) -> Result<Vec<AuditEvent>, AuditError> {
        let events = self.events.read().await;
        let mut results: Vec<AuditEvent> = events
            .iter()
            .filter(|e| {
                if let Some(ref user_id) = filters.user_id {
                    if e.user_id.as_ref() != Some(user_id) {
                        return false;
                    }
                }
                if let Some(ref tenant_id) = filters.tenant_id {
                    if &e.tenant_id != tenant_id {
                        return false;
                    }
                }
                if let Some(ref types) = filters.event_types {
                    if !types.contains(&e.event_type) {
                        return false;
                    }
                }
                if let Some(start) = filters.start_time {
                    if e.timestamp < start {
                        return false;
                    }
                }
                if let Some(end) = filters.end_time {
                    if e.timestamp > end {
                        return false;
                    }
                }
                if let Some(success) = filters.success {
                    if e.success != success {
                        return false;
                    }
                }
                true
            })
            .cloned()
            .collect();

        // Sort by timestamp, newest first
        results.sort_by_key(|a| std::cmp::Reverse(a.timestamp));

        // Apply pagination
        if let Some(offset) = filters.offset {
            if offset < results.len() {
                results = results[offset..].to_vec();
            } else {
                results.clear();
            }
        }

        if let Some(limit) = filters.limit {
            results.truncate(limit);
        }

        Ok(results)
    }

    async fn count(&self, filters: AuditQueryFilters) -> Result<u64, AuditError> {
        let events = self.query(filters).await?;
        Ok(events.len() as u64)
    }
}