Skip to main content

briefcase_core/storage/
mod.rs

1use crate::models::{DecisionSnapshot, Snapshot};
2#[cfg(feature = "async")]
3use async_trait::async_trait;
4use chrono::{DateTime, Utc};
5use std::collections::HashMap;
6use thiserror::Error;
7
8pub mod buffered;
9#[cfg(feature = "sqlite-storage")]
10pub mod sqlite;
11pub mod sync;
12#[cfg(feature = "vcs-storage")]
13pub mod vcs;
14
15#[cfg(feature = "sqlite-storage")]
16pub use sqlite::SqliteBackend;
17#[cfg(feature = "sqlite-storage")]
18pub use sync::SyncSqliteBackend;
19pub use sync::{MemoryStorageBackend, SyncStorageBackend};
20
21#[cfg(feature = "async")]
22#[async_trait]
23pub trait StorageBackend: Send + Sync {
24    /// Save a snapshot, return its ID
25    async fn save(&self, snapshot: &Snapshot) -> Result<String, StorageError>;
26
27    /// Save a single decision snapshot
28    async fn save_decision(&self, decision: &DecisionSnapshot) -> Result<String, StorageError>;
29
30    /// Load a snapshot by ID
31    async fn load(&self, snapshot_id: &str) -> Result<Snapshot, StorageError>;
32
33    /// Load a decision by ID
34    async fn load_decision(&self, decision_id: &str) -> Result<DecisionSnapshot, StorageError>;
35
36    /// Query snapshots with filters
37    async fn query(&self, query: SnapshotQuery) -> Result<Vec<Snapshot>, StorageError>;
38
39    /// Delete a snapshot
40    async fn delete(&self, snapshot_id: &str) -> Result<bool, StorageError>;
41
42    /// Flush pending writes (for batching backends)
43    async fn flush(&self) -> Result<FlushResult, StorageError>;
44
45    /// Check health/connectivity
46    async fn health_check(&self) -> Result<bool, StorageError>;
47}
48
49#[derive(Debug, Clone, Default)]
50pub struct SnapshotQuery {
51    pub function_name: Option<String>,
52    pub module_name: Option<String>,
53    pub model_name: Option<String>,
54    pub start_time: Option<DateTime<Utc>>,
55    pub end_time: Option<DateTime<Utc>>,
56    pub tags: Option<HashMap<String, String>>,
57    pub limit: Option<usize>,
58    pub offset: Option<usize>,
59}
60
61impl SnapshotQuery {
62    pub fn new() -> Self {
63        Self::default()
64    }
65
66    pub fn with_function_name(mut self, function_name: impl Into<String>) -> Self {
67        self.function_name = Some(function_name.into());
68        self
69    }
70
71    pub fn with_module_name(mut self, module_name: impl Into<String>) -> Self {
72        self.module_name = Some(module_name.into());
73        self
74    }
75
76    pub fn with_model_name(mut self, model_name: impl Into<String>) -> Self {
77        self.model_name = Some(model_name.into());
78        self
79    }
80
81    pub fn with_time_range(mut self, start: DateTime<Utc>, end: DateTime<Utc>) -> Self {
82        self.start_time = Some(start);
83        self.end_time = Some(end);
84        self
85    }
86
87    pub fn with_tag(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
88        if self.tags.is_none() {
89            self.tags = Some(HashMap::new());
90        }
91        self.tags.as_mut().unwrap().insert(key.into(), value.into());
92        self
93    }
94
95    pub fn with_limit(mut self, limit: usize) -> Self {
96        self.limit = Some(limit);
97        self
98    }
99
100    pub fn with_offset(mut self, offset: usize) -> Self {
101        self.offset = Some(offset);
102        self
103    }
104}
105
106#[derive(Debug, Clone)]
107pub struct FlushResult {
108    pub snapshots_written: usize,
109    pub bytes_written: usize,
110    pub checkpoint_id: Option<String>,
111}
112
113#[derive(Error, Debug, Clone, PartialEq)]
114pub enum StorageError {
115    #[error("Not found: {0}")]
116    NotFound(String),
117    #[error("Connection error: {0}")]
118    ConnectionError(String),
119    #[error("Serialization error: {0}")]
120    SerializationError(String),
121    #[error("Permission denied: {0}")]
122    PermissionDenied(String),
123    #[error("Quota exceeded")]
124    QuotaExceeded,
125    #[error("Invalid query: {0}")]
126    InvalidQuery(String),
127    #[error("IO error: {0}")]
128    IoError(String),
129}
130
131impl From<serde_json::Error> for StorageError {
132    fn from(err: serde_json::Error) -> Self {
133        StorageError::SerializationError(err.to_string())
134    }
135}
136
137impl From<std::io::Error> for StorageError {
138    fn from(err: std::io::Error) -> Self {
139        StorageError::IoError(err.to_string())
140    }
141}