sz_orm_storage/
tencent.rs1use 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 TencentCosStorage {
9 pub bucket: String,
10 pub region: String,
11 store: Arc<RwLock<HashMap<String, Vec<u8>>>>,
12}
13
14impl TencentCosStorage {
15 pub fn new(bucket: impl Into<String>, region: impl Into<String>) -> Self {
16 Self {
17 bucket: bucket.into(),
18 region: region.into(),
19 store: Arc::new(RwLock::new(HashMap::new())),
20 }
21 }
22
23 pub fn url_for(&self, key: &str) -> String {
24 format!("cos://{}.{}/{}", self.bucket, self.region, key)
25 }
26}
27
28#[async_trait]
29impl Storage for TencentCosStorage {
30 async fn put(
31 &self,
32 key: &str,
33 data: &[u8],
34 _content_type: &str,
35 ) -> Result<String, StorageError> {
36 let mut store = self.store.write().await;
37 store.insert(key.to_string(), data.to_vec());
38 Ok(self.url_for(key))
39 }
40
41 async fn get(&self, key: &str) -> Result<Vec<u8>, StorageError> {
42 let store = self.store.read().await;
43 store
44 .get(key)
45 .cloned()
46 .ok_or_else(|| StorageError::NotFound(format!("cos://{}/{}", self.bucket, key)))
47 }
48
49 async fn delete(&self, key: &str) -> Result<(), StorageError> {
50 let mut store = self.store.write().await;
51 store.remove(key);
52 Ok(())
53 }
54
55 async fn exists(&self, key: &str) -> Result<bool, StorageError> {
56 let store = self.store.read().await;
57 Ok(store.contains_key(key))
58 }
59}
60
61#[cfg(test)]
62mod tests {
63 use super::*;
64
65 #[tokio::test]
66 async fn test_tencent_put_and_get() {
67 let storage = TencentCosStorage::new("bucket-1250000000", "ap-guangzhou");
68 let url = storage
69 .put("a.txt", b"tencent-data", "text/plain")
70 .await
71 .unwrap();
72 assert!(url.starts_with("cos://bucket-1250000000.ap-guangzhou/"));
73 assert!(url.contains("a.txt"));
74
75 let data = storage.get("a.txt").await.unwrap();
76 assert_eq!(data, b"tencent-data");
77 }
78
79 #[tokio::test]
80 async fn test_tencent_get_not_found() {
81 let storage = TencentCosStorage::new("bucket", "ap-guangzhou");
82 let result = storage.get("missing").await;
83 assert!(result.is_err());
84 assert!(matches!(result.unwrap_err(), StorageError::NotFound(_)));
85 }
86
87 #[tokio::test]
88 async fn test_tencent_delete_and_exists() {
89 let storage = TencentCosStorage::new("bucket", "ap-guangzhou");
90 storage.put("key", b"data", "text/plain").await.unwrap();
91 assert!(storage.exists("key").await.unwrap());
92
93 storage.delete("key").await.unwrap();
94 assert!(!storage.exists("key").await.unwrap());
95 }
96
97 #[tokio::test]
98 async fn test_tencent_overwrite() {
99 let storage = TencentCosStorage::new("bucket", "ap-guangzhou");
100 storage.put("key", b"v1", "text/plain").await.unwrap();
101 storage.put("key", b"v2", "text/plain").await.unwrap();
102 assert_eq!(storage.get("key").await.unwrap(), b"v2");
103 }
104
105 #[tokio::test]
106 async fn test_tencent_multiple_keys() {
107 let storage = TencentCosStorage::new("bucket", "ap-guangzhou");
108 storage.put("a", b"a-data", "text/plain").await.unwrap();
109 storage.put("b", b"b-data", "text/plain").await.unwrap();
110
111 assert_eq!(storage.get("a").await.unwrap(), b"a-data");
112 assert_eq!(storage.get("b").await.unwrap(), b"b-data");
113
114 storage.delete("a").await.unwrap();
115 assert!(!storage.exists("a").await.unwrap());
116 assert!(storage.exists("b").await.unwrap());
117 }
118
119 #[tokio::test]
120 async fn test_tencent_url_format() {
121 let storage = TencentCosStorage::new("my-bucket", "ap-beijing");
122 let url = storage.url_for("path/to/file.txt");
123 assert_eq!(url, "cos://my-bucket.ap-beijing/path/to/file.txt");
124 }
125}