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
16pub use types::{
18 AssetKey, AssetKind, CachedAsset, DependencyKind, GemDependency, GemMetadata, IndexStats,
19 SbomCoverage,
20};
21
22pub use quarantine::{
24 calculate_availability, is_version_available, is_version_downloadable, DelayPolicy,
25 GemVersion, QuarantineInfo, QuarantineStats, VersionStatus,
26};
27
28#[cfg(feature = "postgres")]
30pub use postgres::PostgresCacheBackend;
31pub use sqlite::SqliteCacheBackend;
32
33#[derive(Debug, Clone)]
35pub enum CacheBackendKind {
36 Sqlite(SqliteCacheBackend),
37 #[cfg(feature = "postgres")]
38 Postgres(PostgresCacheBackend),
39}
40
41macro_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
212pub(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 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}