briefcase_core/storage/
mod.rs1use 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 async fn save(&self, snapshot: &Snapshot) -> Result<String, StorageError>;
26
27 async fn save_decision(&self, decision: &DecisionSnapshot) -> Result<String, StorageError>;
29
30 async fn load(&self, snapshot_id: &str) -> Result<Snapshot, StorageError>;
32
33 async fn load_decision(&self, decision_id: &str) -> Result<DecisionSnapshot, StorageError>;
35
36 async fn query(&self, query: SnapshotQuery) -> Result<Vec<Snapshot>, StorageError>;
38
39 async fn delete(&self, snapshot_id: &str) -> Result<bool, StorageError>;
41
42 async fn flush(&self) -> Result<FlushResult, StorageError>;
44
45 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}