1use crate::Error;
4use async_trait::async_trait;
5use bytes::Bytes;
6use serde::{Deserialize, Serialize};
7use std::time::SystemTime;
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct FileMetadata {
12 pub path: String,
14 pub size: u64,
16 pub last_modified: Option<SystemTime>,
18 pub mime_type: Option<String>,
20}
21
22impl FileMetadata {
23 pub fn new(path: impl Into<String>, size: u64) -> Self {
25 Self {
26 path: path.into(),
27 size,
28 last_modified: None,
29 mime_type: None,
30 }
31 }
32
33 pub fn with_last_modified(mut self, time: SystemTime) -> Self {
35 self.last_modified = Some(time);
36 self
37 }
38
39 pub fn with_mime_type(mut self, mime: impl Into<String>) -> Self {
41 self.mime_type = Some(mime.into());
42 self
43 }
44}
45
46#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
48#[serde(rename_all = "lowercase")]
49pub enum Visibility {
50 Public,
52 Private,
54}
55
56impl Default for Visibility {
57 fn default() -> Self {
58 Self::Private
59 }
60}
61
62#[derive(Debug, Clone, Default)]
64pub struct PutOptions {
65 pub visibility: Visibility,
67 pub content_type: Option<String>,
69 pub metadata: Option<std::collections::HashMap<String, String>>,
71}
72
73impl PutOptions {
74 pub fn new() -> Self {
76 Self::default()
77 }
78
79 pub fn visibility(mut self, visibility: Visibility) -> Self {
81 self.visibility = visibility;
82 self
83 }
84
85 pub fn content_type(mut self, content_type: impl Into<String>) -> Self {
87 self.content_type = Some(content_type.into());
88 self
89 }
90
91 pub fn public(mut self) -> Self {
93 self.visibility = Visibility::Public;
94 self
95 }
96
97 pub fn private(mut self) -> Self {
99 self.visibility = Visibility::Private;
100 self
101 }
102}
103
104#[async_trait]
106pub trait StorageDriver: Send + Sync {
107 async fn exists(&self, path: &str) -> Result<bool, Error>;
109
110 async fn get(&self, path: &str) -> Result<Bytes, Error>;
112
113 async fn get_string(&self, path: &str) -> Result<String, Error> {
115 let bytes = self.get(path).await?;
116 String::from_utf8(bytes.to_vec()).map_err(|e| Error::Serialization(e.to_string()))
117 }
118
119 async fn put(&self, path: &str, contents: Bytes, options: PutOptions) -> Result<(), Error>;
121
122 async fn put_string(
124 &self,
125 path: &str,
126 contents: &str,
127 options: PutOptions,
128 ) -> Result<(), Error> {
129 self.put(path, Bytes::from(contents.to_string()), options)
130 .await
131 }
132
133 async fn delete(&self, path: &str) -> Result<(), Error>;
135
136 async fn copy(&self, from: &str, to: &str) -> Result<(), Error>;
138
139 async fn rename(&self, from: &str, to: &str) -> Result<(), Error> {
141 self.copy(from, to).await?;
142 self.delete(from).await
143 }
144
145 async fn size(&self, path: &str) -> Result<u64, Error>;
147
148 async fn metadata(&self, path: &str) -> Result<FileMetadata, Error>;
150
151 async fn url(&self, path: &str) -> Result<String, Error>;
153
154 async fn temporary_url(
156 &self,
157 path: &str,
158 expiration: std::time::Duration,
159 ) -> Result<String, Error>;
160
161 async fn files(&self, directory: &str) -> Result<Vec<String>, Error>;
163
164 async fn all_files(&self, directory: &str) -> Result<Vec<String>, Error>;
166
167 async fn directories(&self, directory: &str) -> Result<Vec<String>, Error>;
169
170 async fn make_directory(&self, path: &str) -> Result<(), Error>;
172
173 async fn delete_directory(&self, path: &str) -> Result<(), Error>;
175}
176
177#[cfg(test)]
178mod tests {
179 use super::*;
180
181 #[test]
182 fn test_file_metadata() {
183 let meta = FileMetadata::new("test.txt", 100).with_mime_type("text/plain");
184
185 assert_eq!(meta.path, "test.txt");
186 assert_eq!(meta.size, 100);
187 assert_eq!(meta.mime_type, Some("text/plain".to_string()));
188 }
189
190 #[test]
191 fn test_put_options() {
192 let opts = PutOptions::new().public().content_type("image/png");
193
194 assert_eq!(opts.visibility, Visibility::Public);
195 assert_eq!(opts.content_type, Some("image/png".to_string()));
196 }
197
198 #[test]
199 fn test_visibility_default() {
200 assert_eq!(Visibility::default(), Visibility::Private);
201 }
202}