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, Default, Serialize, Deserialize)]
48#[serde(rename_all = "lowercase")]
49pub enum Visibility {
50 Public,
52 #[default]
54 Private,
55}
56
57#[derive(Debug, Clone, Default)]
59pub struct PutOptions {
60 pub visibility: Visibility,
62 pub content_type: Option<String>,
64 pub metadata: Option<std::collections::HashMap<String, String>>,
66}
67
68impl PutOptions {
69 pub fn new() -> Self {
71 Self::default()
72 }
73
74 pub fn visibility(mut self, visibility: Visibility) -> Self {
76 self.visibility = visibility;
77 self
78 }
79
80 pub fn content_type(mut self, content_type: impl Into<String>) -> Self {
82 self.content_type = Some(content_type.into());
83 self
84 }
85
86 pub fn public(mut self) -> Self {
88 self.visibility = Visibility::Public;
89 self
90 }
91
92 pub fn private(mut self) -> Self {
94 self.visibility = Visibility::Private;
95 self
96 }
97}
98
99#[async_trait]
101pub trait StorageDriver: Send + Sync {
102 async fn exists(&self, path: &str) -> Result<bool, Error>;
104
105 async fn get(&self, path: &str) -> Result<Bytes, Error>;
107
108 async fn get_string(&self, path: &str) -> Result<String, Error> {
110 let bytes = self.get(path).await?;
111 String::from_utf8(bytes.to_vec()).map_err(|e| Error::Serialization(e.to_string()))
112 }
113
114 async fn put(&self, path: &str, contents: Bytes, options: PutOptions) -> Result<(), Error>;
116
117 async fn put_string(
119 &self,
120 path: &str,
121 contents: &str,
122 options: PutOptions,
123 ) -> Result<(), Error> {
124 self.put(path, Bytes::from(contents.to_string()), options)
125 .await
126 }
127
128 async fn delete(&self, path: &str) -> Result<(), Error>;
130
131 async fn copy(&self, from: &str, to: &str) -> Result<(), Error>;
133
134 async fn rename(&self, from: &str, to: &str) -> Result<(), Error> {
136 self.copy(from, to).await?;
137 self.delete(from).await
138 }
139
140 async fn size(&self, path: &str) -> Result<u64, Error>;
142
143 async fn metadata(&self, path: &str) -> Result<FileMetadata, Error>;
145
146 async fn url(&self, path: &str) -> Result<String, Error>;
148
149 async fn temporary_url(
151 &self,
152 path: &str,
153 expiration: std::time::Duration,
154 ) -> Result<String, Error>;
155
156 async fn files(&self, directory: &str) -> Result<Vec<String>, Error>;
158
159 async fn all_files(&self, directory: &str) -> Result<Vec<String>, Error>;
161
162 async fn directories(&self, directory: &str) -> Result<Vec<String>, Error>;
164
165 async fn make_directory(&self, path: &str) -> Result<(), Error>;
167
168 async fn delete_directory(&self, path: &str) -> Result<(), Error>;
170}
171
172#[cfg(test)]
173mod tests {
174 use super::*;
175
176 #[test]
177 fn test_file_metadata() {
178 let meta = FileMetadata::new("test.txt", 100).with_mime_type("text/plain");
179
180 assert_eq!(meta.path, "test.txt");
181 assert_eq!(meta.size, 100);
182 assert_eq!(meta.mime_type, Some("text/plain".to_string()));
183 }
184
185 #[test]
186 fn test_put_options() {
187 let opts = PutOptions::new().public().content_type("image/png");
188
189 assert_eq!(opts.visibility, Visibility::Public);
190 assert_eq!(opts.content_type, Some("image/png".to_string()));
191 }
192
193 #[test]
194 fn test_visibility_default() {
195 assert_eq!(Visibility::default(), Visibility::Private);
196 }
197}