vein_adapter/
cache.rs

1pub mod models;
2#[cfg(feature = "postgres")]
3pub mod postgres;
4pub mod quarantine;
5pub mod serialization;
6pub mod sqlite;
7#[cfg(test)]
8mod tests;
9pub mod types;
10
11use std::future::Future;
12
13use anyhow::Result;
14use chrono::{DateTime, Utc};
15
16// Re-export commonly used types
17pub use types::{
18    AssetKey, AssetKind, CachedAsset, DependencyKind, GemDependency, GemMetadata, IndexStats,
19    SbomCoverage,
20};
21
22// Re-export quarantine types
23pub use quarantine::{
24    calculate_availability, is_version_available, is_version_downloadable, DelayPolicy,
25    GemVersion, QuarantineInfo, QuarantineStats, VersionStatus,
26};
27
28// Re-export backend implementations
29#[cfg(feature = "postgres")]
30pub use postgres::PostgresCacheBackend;
31pub use sqlite::SqliteCacheBackend;
32
33/// Enum dispatch for cache backends - zero-cost abstraction over SQLite/Postgres
34#[derive(Debug, Clone)]
35pub enum CacheBackendKind {
36    Sqlite(SqliteCacheBackend),
37    #[cfg(feature = "postgres")]
38    Postgres(PostgresCacheBackend),
39}
40
41/// Macro to delegate async methods to the inner backend
42macro_rules! delegate {
43    ($self:ident, $method:ident $(, $arg:expr)*) => {
44        match $self {
45            CacheBackendKind::Sqlite(b) => b.$method($($arg),*).await,
46            #[cfg(feature = "postgres")]
47            CacheBackendKind::Postgres(b) => b.$method($($arg),*).await,
48        }
49    };
50}
51
52impl CacheBackendKind {
53    pub async fn get(&self, key: &AssetKey<'_>) -> Result<Option<CachedAsset>> {
54        delegate!(self, get, key)
55    }
56
57    pub async fn insert_or_replace(
58        &self,
59        key: &AssetKey<'_>,
60        path: &str,
61        sha256: &str,
62        size_bytes: u64,
63    ) -> Result<()> {
64        delegate!(self, insert_or_replace, key, path, sha256, size_bytes)
65    }
66
67    pub async fn get_all_gems(&self) -> Result<Vec<(String, String)>> {
68        delegate!(self, get_all_gems)
69    }
70
71    pub async fn stats(&self) -> Result<IndexStats> {
72        delegate!(self, stats)
73    }
74
75    pub async fn catalog_upsert_names(&self, names: &[String]) -> Result<()> {
76        delegate!(self, catalog_upsert_names, names)
77    }
78
79    pub async fn catalog_total(&self) -> Result<u64> {
80        delegate!(self, catalog_total)
81    }
82
83    pub async fn catalog_page(&self, offset: i64, limit: i64) -> Result<Vec<String>> {
84        delegate!(self, catalog_page, offset, limit)
85    }
86
87    pub async fn catalog_meta_get(&self, key: &str) -> Result<Option<String>> {
88        delegate!(self, catalog_meta_get, key)
89    }
90
91    pub async fn catalog_meta_set(&self, key: &str, value: &str) -> Result<()> {
92        delegate!(self, catalog_meta_set, key, value)
93    }
94
95    pub async fn upsert_metadata(&self, metadata: &GemMetadata) -> Result<()> {
96        delegate!(self, upsert_metadata, metadata)
97    }
98
99    pub async fn gem_metadata(
100        &self,
101        name: &str,
102        version: &str,
103        platform: Option<&str>,
104    ) -> Result<Option<GemMetadata>> {
105        delegate!(self, gem_metadata, name, version, platform)
106    }
107
108    pub async fn sbom_coverage(&self) -> Result<SbomCoverage> {
109        delegate!(self, sbom_coverage)
110    }
111
112    pub async fn catalog_languages(&self) -> Result<Vec<String>> {
113        delegate!(self, catalog_languages)
114    }
115
116    pub async fn catalog_page_by_language(
117        &self,
118        language: &str,
119        offset: i64,
120        limit: i64,
121    ) -> Result<Vec<String>> {
122        delegate!(self, catalog_page_by_language, language, offset, limit)
123    }
124
125    pub async fn catalog_total_by_language(&self, language: &str) -> Result<u64> {
126        delegate!(self, catalog_total_by_language, language)
127    }
128
129    pub async fn get_gem_version(
130        &self,
131        name: &str,
132        version: &str,
133        platform: Option<&str>,
134    ) -> Result<Option<GemVersion>> {
135        delegate!(self, get_gem_version, name, version, platform)
136    }
137
138    pub async fn upsert_gem_version(&self, gem_version: &GemVersion) -> Result<()> {
139        delegate!(self, upsert_gem_version, gem_version)
140    }
141
142    pub async fn get_latest_available_version(
143        &self,
144        name: &str,
145        platform: Option<&str>,
146        now: DateTime<Utc>,
147    ) -> Result<Option<GemVersion>> {
148        delegate!(self, get_latest_available_version, name, platform, now)
149    }
150
151    pub async fn get_quarantined_versions(
152        &self,
153        name: &str,
154        now: DateTime<Utc>,
155    ) -> Result<Vec<GemVersion>> {
156        delegate!(self, get_quarantined_versions, name, now)
157    }
158
159    pub async fn update_version_status(
160        &self,
161        name: &str,
162        version: &str,
163        platform: Option<&str>,
164        status: VersionStatus,
165        reason: Option<String>,
166    ) -> Result<()> {
167        delegate!(self, update_version_status, name, version, platform, status, reason)
168    }
169
170    pub async fn promote_expired_quarantines(&self, now: DateTime<Utc>) -> Result<u64> {
171        delegate!(self, promote_expired_quarantines, now)
172    }
173
174    pub async fn mark_yanked(&self, name: &str, version: &str) -> Result<()> {
175        delegate!(self, mark_yanked, name, version)
176    }
177
178    pub async fn get_all_quarantined(&self, limit: u32, offset: u32) -> Result<Vec<GemVersion>> {
179        delegate!(self, get_all_quarantined, limit, offset)
180    }
181
182    pub async fn quarantine_stats(&self) -> Result<QuarantineStats> {
183        delegate!(self, quarantine_stats)
184    }
185
186    pub async fn get_gem_versions_for_index(&self, name: &str) -> Result<Vec<GemVersion>> {
187        delegate!(self, get_gem_versions_for_index, name)
188    }
189
190    pub async fn quarantine_table_exists(&self) -> Result<bool> {
191        delegate!(self, quarantine_table_exists)
192    }
193
194    pub async fn run_quarantine_migrations(&self) -> Result<()> {
195        delegate!(self, run_quarantine_migrations)
196    }
197}
198
199#[cfg(feature = "postgres")]
200impl From<PostgresCacheBackend> for CacheBackendKind {
201    fn from(backend: PostgresCacheBackend) -> Self {
202        CacheBackendKind::Postgres(backend)
203    }
204}
205
206impl From<SqliteCacheBackend> for CacheBackendKind {
207    fn from(backend: SqliteCacheBackend) -> Self {
208        CacheBackendKind::Sqlite(backend)
209    }
210}
211
212// Internal trait for concrete implementations - public API uses CacheBackendKind enum
213// Uses explicit `impl Future + Send` to satisfy auto-trait bounds without #[allow]
214pub(crate) trait CacheBackend: Send + Sync {
215    fn get(&self, key: &AssetKey<'_>) -> impl Future<Output = Result<Option<CachedAsset>>> + Send;
216    fn insert_or_replace(
217        &self,
218        key: &AssetKey<'_>,
219        path: &str,
220        sha256: &str,
221        size_bytes: u64,
222    ) -> impl Future<Output = Result<()>> + Send;
223    fn get_all_gems(&self) -> impl Future<Output = Result<Vec<(String, String)>>> + Send;
224    fn stats(&self) -> impl Future<Output = Result<IndexStats>> + Send;
225    fn catalog_upsert_names(&self, names: &[String]) -> impl Future<Output = Result<()>> + Send;
226    fn catalog_total(&self) -> impl Future<Output = Result<u64>> + Send;
227    fn catalog_page(&self, offset: i64, limit: i64)
228        -> impl Future<Output = Result<Vec<String>>> + Send;
229    fn catalog_meta_get(&self, key: &str)
230        -> impl Future<Output = Result<Option<String>>> + Send;
231    fn catalog_meta_set(&self, key: &str, value: &str) -> impl Future<Output = Result<()>> + Send;
232    fn upsert_metadata(&self, metadata: &GemMetadata) -> impl Future<Output = Result<()>> + Send;
233    fn gem_metadata(
234        &self,
235        name: &str,
236        version: &str,
237        platform: Option<&str>,
238    ) -> impl Future<Output = Result<Option<GemMetadata>>> + Send;
239    fn sbom_coverage(&self) -> impl Future<Output = Result<SbomCoverage>> + Send;
240    fn catalog_languages(&self) -> impl Future<Output = Result<Vec<String>>> + Send;
241    fn catalog_page_by_language(
242        &self,
243        language: &str,
244        offset: i64,
245        limit: i64,
246    ) -> impl Future<Output = Result<Vec<String>>> + Send;
247    fn catalog_total_by_language(&self, language: &str)
248        -> impl Future<Output = Result<u64>> + Send;
249
250    // ==================== Quarantine Methods ====================
251
252    fn get_gem_version(
253        &self,
254        name: &str,
255        version: &str,
256        platform: Option<&str>,
257    ) -> impl Future<Output = Result<Option<GemVersion>>> + Send;
258
259    fn upsert_gem_version(
260        &self,
261        gem_version: &GemVersion,
262    ) -> impl Future<Output = Result<()>> + Send;
263
264    fn get_latest_available_version(
265        &self,
266        name: &str,
267        platform: Option<&str>,
268        now: DateTime<Utc>,
269    ) -> impl Future<Output = Result<Option<GemVersion>>> + Send;
270
271    fn get_quarantined_versions(
272        &self,
273        name: &str,
274        now: DateTime<Utc>,
275    ) -> impl Future<Output = Result<Vec<GemVersion>>> + Send;
276
277    fn update_version_status(
278        &self,
279        name: &str,
280        version: &str,
281        platform: Option<&str>,
282        status: VersionStatus,
283        reason: Option<String>,
284    ) -> impl Future<Output = Result<()>> + Send;
285
286    fn promote_expired_quarantines(
287        &self,
288        now: DateTime<Utc>,
289    ) -> impl Future<Output = Result<u64>> + Send;
290
291    fn mark_yanked(&self, name: &str, version: &str) -> impl Future<Output = Result<()>> + Send;
292
293    fn get_all_quarantined(
294        &self,
295        limit: u32,
296        offset: u32,
297    ) -> impl Future<Output = Result<Vec<GemVersion>>> + Send;
298
299    fn quarantine_stats(&self) -> impl Future<Output = Result<QuarantineStats>> + Send;
300
301    fn get_gem_versions_for_index(
302        &self,
303        name: &str,
304    ) -> impl Future<Output = Result<Vec<GemVersion>>> + Send;
305
306    fn quarantine_table_exists(&self) -> impl Future<Output = Result<bool>> + Send;
307
308    fn run_quarantine_migrations(&self) -> impl Future<Output = Result<()>> + Send;
309}