1use async_trait::async_trait;
4use bytes::Bytes;
5use chrono::{DateTime, Utc};
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8use std::fmt;
9
10use crate::error::Result;
11
12#[async_trait]
14pub trait CloudStorage: Send + Sync {
15 async fn upload(&self, key: &str, data: Bytes) -> Result<()>;
17
18 async fn upload_with_options(
20 &self,
21 key: &str,
22 data: Bytes,
23 options: UploadOptions,
24 ) -> Result<()>;
25
26 async fn download(&self, key: &str) -> Result<Bytes>;
28
29 async fn download_range(&self, key: &str, start: u64, end: u64) -> Result<Bytes>;
31
32 async fn list(&self, prefix: &str) -> Result<Vec<ObjectInfo>>;
34
35 async fn list_paginated(
37 &self,
38 prefix: &str,
39 continuation_token: Option<String>,
40 max_keys: usize,
41 ) -> Result<ListResult>;
42
43 async fn delete(&self, key: &str) -> Result<()>;
45
46 async fn delete_batch(&self, keys: &[String]) -> Result<Vec<DeleteResult>>;
48
49 async fn get_metadata(&self, key: &str) -> Result<ObjectMetadata>;
51
52 async fn update_metadata(&self, key: &str, metadata: HashMap<String, String>) -> Result<()>;
54
55 async fn exists(&self, key: &str) -> Result<bool>;
57
58 async fn copy(&self, source_key: &str, dest_key: &str) -> Result<()>;
60
61 async fn presigned_download_url(&self, key: &str, expires_in_secs: u64) -> Result<String>;
63
64 async fn presigned_upload_url(&self, key: &str, expires_in_secs: u64) -> Result<String>;
66
67 async fn set_storage_class(&self, key: &str, class: StorageClass) -> Result<()>;
69
70 async fn get_stats(&self, prefix: &str) -> Result<StorageStats>;
72}
73
74#[derive(Debug, Clone, Serialize, Deserialize)]
76pub struct ObjectInfo {
77 pub key: String,
79 pub size: u64,
81 pub last_modified: DateTime<Utc>,
83 pub etag: Option<String>,
85 pub storage_class: Option<StorageClass>,
87 pub content_type: Option<String>,
89}
90
91#[derive(Debug, Clone, Serialize, Deserialize)]
93pub struct ObjectMetadata {
94 pub info: ObjectInfo,
96 pub user_metadata: HashMap<String, String>,
98 pub system_metadata: HashMap<String, String>,
100 pub tags: HashMap<String, String>,
102 pub content_encoding: Option<String>,
104 pub content_language: Option<String>,
106 pub cache_control: Option<String>,
108 pub content_disposition: Option<String>,
110}
111
112#[derive(Debug, Clone)]
114pub struct ListResult {
115 pub objects: Vec<ObjectInfo>,
117 pub continuation_token: Option<String>,
119 pub is_truncated: bool,
121 pub common_prefixes: Vec<String>,
123}
124
125#[derive(Debug, Clone)]
127pub struct DeleteResult {
128 pub key: String,
130 pub success: bool,
132 pub error: Option<String>,
134}
135
136#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
138pub enum StorageClass {
139 Standard,
141 InfrequentAccess,
143 Glacier,
145 DeepArchive,
147 IntelligentTiering,
149 OneZoneIA,
151 ReducedRedundancy,
153}
154
155impl fmt::Display for StorageClass {
156 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
157 match self {
158 StorageClass::Standard => write!(f, "STANDARD"),
159 StorageClass::InfrequentAccess => write!(f, "STANDARD_IA"),
160 StorageClass::Glacier => write!(f, "GLACIER"),
161 StorageClass::DeepArchive => write!(f, "DEEP_ARCHIVE"),
162 StorageClass::IntelligentTiering => write!(f, "INTELLIGENT_TIERING"),
163 StorageClass::OneZoneIA => write!(f, "ONEZONE_IA"),
164 StorageClass::ReducedRedundancy => write!(f, "REDUCED_REDUNDANCY"),
165 }
166 }
167}
168
169#[derive(Debug, Clone, Default)]
171pub struct UploadOptions {
172 pub content_type: Option<String>,
174 pub content_encoding: Option<String>,
176 pub cache_control: Option<String>,
178 pub content_disposition: Option<String>,
180 pub metadata: HashMap<String, String>,
182 pub tags: HashMap<String, String>,
184 pub storage_class: Option<StorageClass>,
186 pub encryption: Option<String>,
188 pub acl: Option<String>,
190}
191
192#[derive(Debug, Clone, Default)]
194pub struct StorageStats {
195 pub total_size: u64,
197 pub object_count: u64,
199 pub size_by_class: HashMap<String, u64>,
201 pub count_by_class: HashMap<String, u64>,
203}
204
205#[derive(Debug, Clone)]
207pub struct TransferProgress {
208 pub bytes_transferred: u64,
210 pub total_bytes: u64,
212 pub rate_bps: f64,
214 pub eta_secs: Option<f64>,
216}
217
218impl TransferProgress {
219 #[must_use]
221 pub fn percentage(&self) -> f64 {
222 if self.total_bytes == 0 {
223 0.0
224 } else {
225 (self.bytes_transferred as f64 / self.total_bytes as f64) * 100.0
226 }
227 }
228
229 #[must_use]
231 pub fn is_complete(&self) -> bool {
232 self.bytes_transferred >= self.total_bytes
233 }
234}
235
236#[derive(Debug, Clone, Serialize, Deserialize)]
238pub struct LifecycleRule {
239 pub id: String,
241 pub enabled: bool,
243 pub prefix: Option<String>,
245 pub tags: HashMap<String, String>,
247 pub transitions: Vec<Transition>,
249 pub expiration: Option<Expiration>,
251}
252
253#[derive(Debug, Clone, Serialize, Deserialize)]
255pub struct Transition {
256 pub days: u32,
258 pub storage_class: StorageClass,
260}
261
262#[derive(Debug, Clone, Serialize, Deserialize)]
264pub struct Expiration {
265 pub days: Option<u32>,
267 pub date: Option<DateTime<Utc>>,
269 pub expired_object_delete_marker: bool,
271}
272
273#[derive(Debug, Clone, Serialize, Deserialize)]
275pub struct ReplicationConfig {
276 pub role: Option<String>,
278 pub destination_bucket: String,
280 pub destination_storage_class: Option<StorageClass>,
282 pub rules: Vec<ReplicationRule>,
284}
285
286#[derive(Debug, Clone, Serialize, Deserialize)]
288pub struct ReplicationRule {
289 pub id: String,
291 pub enabled: bool,
293 pub prefix: Option<String>,
295 pub priority: i32,
297 pub delete_marker_replication: bool,
299}