uvb-storage-api 0.2.1

Storage backend trait abstractions for UVB data persistence
Documentation
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use std::time::SystemTime;
use thiserror::Error;
use uvb_core::{Context, Subject};

#[derive(Debug, Error)]
pub enum TransactionError {
    #[error("transaction not found")]
    NotFound,
    #[error("transaction expired")]
    Expired,
    #[error("storage error: {0}")]
    Storage(String),
    #[error("serialization error: {0}")]
    Serialization(String),
}

/// Represents a verification transaction in storage
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct TransactionRecord {
    pub id: String,
    pub subject: Subject,
    pub context: Context,
    pub status: TransactionStatus,
    pub policy_id: String,
    pub completed_factors: Vec<String>,
    pub pending_challenges: serde_json::Value,
    pub metadata: serde_json::Value,
    pub created_at: SystemTime,
    pub expires_at: SystemTime,
    pub updated_at: SystemTime,
}

#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub enum TransactionStatus {
    InProgress,
    Succeeded,
    Failed,
    Expired,
}

/// Trait for pluggable transaction storage backends
///
/// Implementations might be:
/// - In-memory (HashMap)
/// - PostgreSQL
/// - Redis
/// - DynamoDB
/// - Any custom storage system
#[async_trait]
pub trait TransactionStore: Send + Sync {
    /// Create a new transaction
    async fn create(&self, record: TransactionRecord) -> Result<(), TransactionError>;

    /// Get a transaction by ID
    async fn get(&self, id: &str) -> Result<Option<TransactionRecord>, TransactionError>;

    /// Update an existing transaction
    async fn update(&self, record: TransactionRecord) -> Result<(), TransactionError>;

    /// Delete a transaction
    async fn delete(&self, id: &str) -> Result<(), TransactionError>;

    /// List transactions by user_id (for admin/debugging)
    async fn list_by_user(
        &self,
        user_id: &str,
        limit: usize,
    ) -> Result<Vec<TransactionRecord>, TransactionError>;

    /// Cleanup expired transactions (for background job)
    async fn cleanup_expired(&self) -> Result<usize, TransactionError>;
}