Skip to main content

sz_orm_storage/
qiniu.rs

1use crate::error::StorageError;
2use crate::storage::Storage;
3use async_trait::async_trait;
4use std::collections::HashMap;
5use std::sync::Arc;
6use tokio::sync::RwLock;
7
8pub struct QiniuKodoStorage {
9    pub bucket: String,
10    store: Arc<RwLock<HashMap<String, Vec<u8>>>>,
11}
12
13impl QiniuKodoStorage {
14    pub fn new(bucket: impl Into<String>) -> Self {
15        Self {
16            bucket: bucket.into(),
17            store: Arc::new(RwLock::new(HashMap::new())),
18        }
19    }
20
21    pub fn url_for(&self, key: &str) -> String {
22        format!("qiniu://{}/{}", self.bucket, key)
23    }
24}
25
26#[async_trait]
27impl Storage for QiniuKodoStorage {
28    async fn put(
29        &self,
30        key: &str,
31        data: &[u8],
32        _content_type: &str,
33    ) -> Result<String, StorageError> {
34        let mut store = self.store.write().await;
35        store.insert(key.to_string(), data.to_vec());
36        Ok(self.url_for(key))
37    }
38
39    async fn get(&self, key: &str) -> Result<Vec<u8>, StorageError> {
40        let store = self.store.read().await;
41        store
42            .get(key)
43            .cloned()
44            .ok_or_else(|| StorageError::NotFound(format!("qiniu://{}/{}", self.bucket, key)))
45    }
46
47    async fn delete(&self, key: &str) -> Result<(), StorageError> {
48        let mut store = self.store.write().await;
49        store.remove(key);
50        Ok(())
51    }
52
53    async fn exists(&self, key: &str) -> Result<bool, StorageError> {
54        let store = self.store.read().await;
55        Ok(store.contains_key(key))
56    }
57}
58
59#[cfg(test)]
60mod tests {
61    use super::*;
62
63    #[tokio::test]
64    async fn test_qiniu_put_and_get() {
65        let storage = QiniuKodoStorage::new("my-bucket");
66        let url = storage
67            .put("file.txt", b"qiniu-data", "text/plain")
68            .await
69            .unwrap();
70        assert!(url.starts_with("qiniu://my-bucket/"));
71        assert!(url.contains("file.txt"));
72
73        let data = storage.get("file.txt").await.unwrap();
74        assert_eq!(data, b"qiniu-data");
75    }
76
77    #[tokio::test]
78    async fn test_qiniu_get_not_found() {
79        let storage = QiniuKodoStorage::new("bucket");
80        let result = storage.get("missing").await;
81        assert!(result.is_err());
82        assert!(matches!(result.unwrap_err(), StorageError::NotFound(_)));
83    }
84
85    #[tokio::test]
86    async fn test_qiniu_delete_and_exists() {
87        let storage = QiniuKodoStorage::new("bucket");
88        storage.put("key", b"data", "text/plain").await.unwrap();
89        assert!(storage.exists("key").await.unwrap());
90
91        storage.delete("key").await.unwrap();
92        assert!(!storage.exists("key").await.unwrap());
93    }
94
95    #[tokio::test]
96    async fn test_qiniu_overwrite() {
97        let storage = QiniuKodoStorage::new("bucket");
98        storage.put("key", b"v1", "text/plain").await.unwrap();
99        storage.put("key", b"v2", "text/plain").await.unwrap();
100        assert_eq!(storage.get("key").await.unwrap(), b"v2");
101    }
102
103    #[tokio::test]
104    async fn test_qiniu_url_format() {
105        let storage = QiniuKodoStorage::new("my-bucket");
106        assert_eq!(storage.url_for("file.txt"), "qiniu://my-bucket/file.txt");
107        assert_eq!(storage.url_for("a/b/c.txt"), "qiniu://my-bucket/a/b/c.txt");
108    }
109}