Skip to main content

perfgate_server/storage/
mod.rs

1//! Storage trait and implementations for baseline persistence.
2//!
3//! This module provides the [`BaselineStore`] trait for abstracting storage
4//! operations and implementations for different backends.
5
6mod artifacts;
7mod memory;
8mod postgres;
9mod sqlite;
10
11pub use artifacts::ObjectArtifactStore;
12pub use memory::InMemoryStore;
13pub use postgres::PostgresStore;
14pub use sqlite::SqliteStore;
15
16use crate::error::StoreError;
17use crate::models::{
18    BaselineRecord, BaselineVersion, ListBaselinesQuery, ListBaselinesResponse, ListVerdictsQuery,
19    ListVerdictsResponse, VerdictRecord,
20};
21use async_trait::async_trait;
22
23/// Trait for storing raw artifacts (receipts).
24#[async_trait]
25pub trait ArtifactStore: std::fmt::Debug + Send + Sync {
26    /// Stores an artifact at the given path.
27    async fn put(&self, path: &str, data: Vec<u8>) -> Result<(), StoreError>;
28
29    /// Retrieves an artifact from the given path.
30    async fn get(&self, path: &str) -> Result<Vec<u8>, StoreError>;
31
32    /// Deletes an artifact from the given path.
33    async fn delete(&self, path: &str) -> Result<(), StoreError>;
34}
35
36/// Trait for baseline storage operations.
37///
38/// This trait abstracts the storage layer, allowing different backends
39/// (in-memory, SQLite, PostgreSQL) to be used interchangeably.
40#[async_trait]
41pub trait BaselineStore: Send + Sync {
42    /// Stores a new baseline record.
43    async fn create(&self, record: &BaselineRecord) -> Result<(), StoreError>;
44
45    /// Retrieves a baseline by project, benchmark, and version.
46    async fn get(
47        &self,
48        project: &str,
49        benchmark: &str,
50        version: &str,
51    ) -> Result<Option<BaselineRecord>, StoreError>;
52
53    /// Retrieves the latest baseline for a project and benchmark.
54    async fn get_latest(
55        &self,
56        project: &str,
57        benchmark: &str,
58    ) -> Result<Option<BaselineRecord>, StoreError>;
59
60    /// Lists baselines with optional filtering.
61    async fn list(
62        &self,
63        project: &str,
64        query: &ListBaselinesQuery,
65    ) -> Result<ListBaselinesResponse, StoreError>;
66
67    /// Updates an existing baseline record.
68    async fn update(&self, record: &BaselineRecord) -> Result<(), StoreError>;
69
70    /// Deletes a baseline (soft delete).
71    async fn delete(
72        &self,
73        project: &str,
74        benchmark: &str,
75        version: &str,
76    ) -> Result<bool, StoreError>;
77
78    /// Permanently removes a deleted baseline.
79    async fn hard_delete(
80        &self,
81        project: &str,
82        benchmark: &str,
83        version: &str,
84    ) -> Result<bool, StoreError>;
85
86    /// Lists all versions for a benchmark.
87    async fn list_versions(
88        &self,
89        project: &str,
90        benchmark: &str,
91    ) -> Result<Vec<BaselineVersion>, StoreError>;
92
93    /// Checks if the storage backend is healthy.
94    async fn health_check(&self) -> Result<StorageHealth, StoreError>;
95
96    /// Returns the backend type name.
97    fn backend_type(&self) -> &'static str;
98
99    /// Stores a new verdict record.
100    async fn create_verdict(&self, record: &VerdictRecord) -> Result<(), StoreError>;
101
102    /// Lists verdicts with optional filtering.
103    async fn list_verdicts(
104        &self,
105        project: &str,
106        query: &ListVerdictsQuery,
107    ) -> Result<ListVerdictsResponse, StoreError>;
108}
109
110/// Storage backend health status.
111#[derive(Debug, Clone, Copy, PartialEq, Eq)]
112pub enum StorageHealth {
113    /// Storage is healthy and operational
114    Healthy,
115    /// Storage is degraded but functional
116    Degraded,
117    /// Storage is unavailable
118    Unhealthy,
119}
120
121impl StorageHealth {
122    /// Returns the string representation.
123    pub fn as_str(&self) -> &'static str {
124        match self {
125            Self::Healthy => "healthy",
126            Self::Degraded => "degraded",
127            Self::Unhealthy => "unhealthy",
128        }
129    }
130}
131
132impl std::fmt::Display for StorageHealth {
133    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
134        write!(f, "{}", self.as_str())
135    }
136}