Skip to main content

sz_orm_storage/
huawei.rs

1//! # Huawei OBS Storage(**MOCK-ONLY,非生产可用**)
2//!
3//! ⚠️ **重要警告:本模块为内存 Mock 实现,未集成真实华为云 OBS SDK。**
4//!
5//! - 所有数据存储在进程内 `HashMap`,重启即丢失
6//! - 不执行任何 HTTP 请求,不与真实 OBS 服务交互
7//! - 不支持认证、签名、TLS、分片上传等任何 OBS 生产特性
8//! - **请勿用于生产环境**——仅适用于单元测试与本地开发
9//!
10//! 如需真实 OBS 集成,请基于 [`crate::storage::Storage`] trait
11//! 接入华为云官方 SDK 实现。
12
13use crate::error::StorageError;
14use crate::storage::Storage;
15use async_trait::async_trait;
16use std::collections::HashMap;
17use std::sync::Arc;
18use tokio::sync::RwLock;
19
20/// 华为云 OBS 存储后端(**Mock 实现**)
21///
22/// ⚠️ 仅用于测试。所有数据存储在内存 `HashMap`,不与真实 OBS 服务交互。
23pub struct HuaweiObsStorage {
24    pub bucket: String,
25    pub endpoint: String,
26    store: Arc<RwLock<HashMap<String, Vec<u8>>>>,
27}
28
29impl HuaweiObsStorage {
30    pub fn new(bucket: impl Into<String>, endpoint: impl Into<String>) -> Self {
31        Self {
32            bucket: bucket.into(),
33            endpoint: endpoint.into(),
34            store: Arc::new(RwLock::new(HashMap::new())),
35        }
36    }
37
38    pub fn url_for(&self, key: &str) -> String {
39        format!("obs://{}.{}/{}", self.bucket, self.endpoint, key)
40    }
41}
42
43#[async_trait]
44impl Storage for HuaweiObsStorage {
45    async fn put(
46        &self,
47        key: &str,
48        data: &[u8],
49        _content_type: &str,
50    ) -> Result<String, StorageError> {
51        let mut store = self.store.write().await;
52        store.insert(key.to_string(), data.to_vec());
53        Ok(self.url_for(key))
54    }
55
56    async fn get(&self, key: &str) -> Result<Vec<u8>, StorageError> {
57        let store = self.store.read().await;
58        store
59            .get(key)
60            .cloned()
61            .ok_or_else(|| StorageError::NotFound(format!("obs://{}/{}", self.bucket, key)))
62    }
63
64    async fn delete(&self, key: &str) -> Result<(), StorageError> {
65        let mut store = self.store.write().await;
66        store.remove(key);
67        Ok(())
68    }
69
70    async fn exists(&self, key: &str) -> Result<bool, StorageError> {
71        let store = self.store.read().await;
72        Ok(store.contains_key(key))
73    }
74}
75
76#[cfg(test)]
77mod tests {
78    use super::*;
79
80    #[tokio::test]
81    async fn test_huawei_put_and_get() {
82        let storage = HuaweiObsStorage::new("my-bucket", "obs.cn-north-4.myhuaweicloud.com");
83        let url = storage
84            .put("a.txt", b"huawei-data", "text/plain")
85            .await
86            .unwrap();
87        assert!(url.starts_with("obs://my-bucket.obs.cn-north-4.myhuaweicloud.com/"));
88
89        let data = storage.get("a.txt").await.unwrap();
90        assert_eq!(data, b"huawei-data");
91    }
92
93    #[tokio::test]
94    async fn test_huawei_get_not_found() {
95        let storage = HuaweiObsStorage::new("bucket", "obs.cn-north-4.myhuaweicloud.com");
96        let result = storage.get("missing").await;
97        assert!(result.is_err());
98        assert!(matches!(result.unwrap_err(), StorageError::NotFound(_)));
99    }
100
101    #[tokio::test]
102    async fn test_huawei_delete_and_exists() {
103        let storage = HuaweiObsStorage::new("bucket", "obs.cn-north-4.myhuaweicloud.com");
104        storage.put("key", b"data", "text/plain").await.unwrap();
105        assert!(storage.exists("key").await.unwrap());
106
107        storage.delete("key").await.unwrap();
108        assert!(!storage.exists("key").await.unwrap());
109    }
110
111    #[tokio::test]
112    async fn test_huawei_overwrite() {
113        let storage = HuaweiObsStorage::new("bucket", "obs.cn-north-4.myhuaweicloud.com");
114        storage.put("key", b"v1", "text/plain").await.unwrap();
115        storage.put("key", b"v2", "text/plain").await.unwrap();
116        assert_eq!(storage.get("key").await.unwrap(), b"v2");
117    }
118
119    #[tokio::test]
120    async fn test_huawei_multiple_keys() {
121        let storage = HuaweiObsStorage::new("bucket", "obs.cn-north-4.myhuaweicloud.com");
122        storage.put("a", b"a-data", "text/plain").await.unwrap();
123        storage.put("b", b"b-data", "text/plain").await.unwrap();
124
125        assert_eq!(storage.get("a").await.unwrap(), b"a-data");
126        assert_eq!(storage.get("b").await.unwrap(), b"b-data");
127
128        storage.delete("a").await.unwrap();
129        assert!(!storage.exists("a").await.unwrap());
130        assert!(storage.exists("b").await.unwrap());
131    }
132}