Skip to main content

orion_accessor/update/
upload_options.rs

1use crate::prelude::*;
2use std::str::FromStr;
3
4/// HTTP methods supported for upload operations
5#[derive(Debug, Clone, PartialEq, Default, derive_more::Display)]
6pub enum HttpMethod {
7    #[display("PUT")]
8    /// PUT request for binary upload
9    #[default]
10    Put,
11    #[display("POST")]
12    /// POST request for form-data upload
13    Post,
14    #[display("PATCH")]
15    /// PATCH request for partial updates
16    Patch,
17}
18
19/// 转换错误类型
20#[derive(Debug, thiserror::Error)]
21#[error("无效的HTTP方法: {0}")]
22pub struct ParseHttpMethodError(String);
23
24impl FromStr for HttpMethod {
25    type Err = ParseHttpMethodError;
26
27    fn from_str(s: &str) -> Result<Self, Self::Err> {
28        match s.to_uppercase().as_str() {
29            "PUT" => Ok(HttpMethod::Put),
30            "POST" => Ok(HttpMethod::Post),
31            "PATCH" => Ok(HttpMethod::Patch),
32            _ => Err(ParseHttpMethodError(s.to_string())),
33        }
34    }
35}
36
37impl TryFrom<&str> for HttpMethod {
38    type Error = ParseHttpMethodError;
39
40    fn try_from(value: &str) -> Result<Self, Self::Error> {
41        HttpMethod::from_str(value)
42    }
43}
44
45impl TryFrom<String> for HttpMethod {
46    type Error = ParseHttpMethodError;
47
48    fn try_from(value: String) -> Result<Self, Self::Error> {
49        HttpMethod::from_str(&value)
50    }
51}
52
53/// Options for controlling upload operations, primarily focused on HTTP uploads
54#[derive(Clone, Debug, Default)]
55pub struct UploadOptions {
56    /// HTTP method to use for upload
57    http_method: HttpMethod,
58    /// Whether to compress the resource before upload
59    compression: bool,
60    /// Additional metadata to include in upload headers
61    metadata: ValueDict,
62}
63
64impl UploadOptions {
65    /// Create new UploadOptions with default settings
66    pub fn new() -> Self {
67        Self::default()
68    }
69
70    /// Create UploadOptions for specific HTTP method
71    pub fn with_method(method: HttpMethod) -> Self {
72        Self {
73            http_method: method,
74            compression: false,
75            metadata: ValueDict::default(),
76        }
77    }
78
79    /// Set HTTP method for upload
80    pub fn method(mut self, method: HttpMethod) -> Self {
81        self.http_method = method;
82        self
83    }
84
85    /// Enable or disable compression
86    pub fn compression(mut self, enable: bool) -> Self {
87        self.compression = enable;
88        self
89    }
90
91    /// Add metadata to upload
92    /// 添加自定义元数据
93    pub fn metadata(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
94        self.metadata
95            .insert(key.into(), ValueType::String(value.into()));
96        self
97    }
98
99    /// Get the HTTP method
100    pub fn http_method(&self) -> &HttpMethod {
101        &self.http_method
102    }
103
104    /// Check if compression is enabled
105    pub fn compression_enabled(&self) -> bool {
106        self.compression
107    }
108
109    /// Get metadata
110    pub fn metadata_dict(&self) -> &ValueDict {
111        &self.metadata
112    }
113
114    /// Create for testing purposes
115    pub fn for_test() -> Self {
116        Self {
117            http_method: HttpMethod::Put,
118            compression: false,
119            metadata: ValueDict::default(),
120        }
121    }
122}
123
124impl From<(usize, ValueDict)> for UploadOptions {
125    fn from((method, values): (usize, ValueDict)) -> Self {
126        let http_method = match method {
127            0 => HttpMethod::Put,
128            1 => HttpMethod::Post,
129            2 => HttpMethod::Patch,
130            _ => HttpMethod::Put,
131        };
132
133        Self {
134            http_method,
135            compression: false,
136            metadata: values,
137        }
138    }
139}
140
141impl From<crate::update::DownloadOptions> for UploadOptions {
142    fn from(_: crate::update::DownloadOptions) -> Self {
143        Self::default()
144    }
145}
146
147#[cfg(test)]
148mod tests {
149    use super::*;
150    use std::str::FromStr;
151
152    #[test]
153    fn test_from_str_valid_cases() {
154        assert_eq!(HttpMethod::from_str("PUT").unwrap(), HttpMethod::Put);
155        assert_eq!(HttpMethod::from_str("put").unwrap(), HttpMethod::Put);
156        assert_eq!(HttpMethod::from_str("Put").unwrap(), HttpMethod::Put);
157
158        assert_eq!(HttpMethod::from_str("POST").unwrap(), HttpMethod::Post);
159        assert_eq!(HttpMethod::from_str("post").unwrap(), HttpMethod::Post);
160        assert_eq!(HttpMethod::from_str("Post").unwrap(), HttpMethod::Post);
161
162        assert_eq!(HttpMethod::from_str("PATCH").unwrap(), HttpMethod::Patch);
163        assert_eq!(HttpMethod::from_str("patch").unwrap(), HttpMethod::Patch);
164        assert_eq!(HttpMethod::from_str("Patch").unwrap(), HttpMethod::Patch);
165    }
166
167    #[test]
168    fn test_from_str_invalid_cases() {
169        assert!(HttpMethod::from_str("GET").is_err());
170        assert!(HttpMethod::from_str("DELETE").is_err());
171        assert!(HttpMethod::from_str("INVALID").is_err());
172        assert!(HttpMethod::from_str("").is_err());
173        assert!(HttpMethod::from_str("  PUT  ").is_err()); // 包含空格
174    }
175
176    #[test]
177    fn test_try_from_str() {
178        assert_eq!(HttpMethod::try_from("PUT").unwrap(), HttpMethod::Put);
179        assert_eq!(HttpMethod::try_from("Post").unwrap(), HttpMethod::Post);
180        assert_eq!(HttpMethod::try_from("patch").unwrap(), HttpMethod::Patch);
181    }
182
183    #[test]
184    fn test_try_from_string() {
185        assert_eq!(
186            HttpMethod::try_from(String::from("PUT")).unwrap(),
187            HttpMethod::Put
188        );
189        assert_eq!(
190            HttpMethod::try_from(String::from("POST")).unwrap(),
191            HttpMethod::Post
192        );
193        assert_eq!(
194            HttpMethod::try_from(String::from("PATCH")).unwrap(),
195            HttpMethod::Patch
196        );
197
198        assert!(HttpMethod::try_from(String::from("DELETE")).is_err());
199        assert!(HttpMethod::try_from(String::from("")).is_err());
200    }
201
202    #[test]
203    fn test_parse_http_method_error() {
204        let err = HttpMethod::from_str("INVALID").unwrap_err();
205        assert_eq!(err.to_string(), "无效的HTTP方法: INVALID");
206
207        let err = HttpMethod::from_str("").unwrap_err();
208        assert_eq!(err.to_string(), "无效的HTTP方法: ");
209    }
210
211    #[test]
212    fn test_http_method_display() {
213        assert_eq!(HttpMethod::Put.to_string(), "PUT");
214        assert_eq!(HttpMethod::Post.to_string(), "POST");
215        assert_eq!(HttpMethod::Patch.to_string(), "PATCH");
216    }
217
218    #[test]
219    fn test_http_method_default() {
220        let method = HttpMethod::default();
221        assert_eq!(method, HttpMethod::Put);
222    }
223
224    #[test]
225    fn test_http_method_clone() {
226        let original = HttpMethod::Post;
227        let cloned = original.clone();
228        assert_eq!(original, cloned);
229    }
230
231    #[test]
232    fn test_http_method_partial_eq() {
233        assert_eq!(HttpMethod::Put, HttpMethod::Put);
234        assert_ne!(HttpMethod::Put, HttpMethod::Post);
235        assert_ne!(HttpMethod::Post, HttpMethod::Patch);
236        assert_ne!(HttpMethod::Patch, HttpMethod::Put);
237    }
238
239    #[test]
240    fn test_upload_options_new() {
241        let options = UploadOptions::new();
242        assert_eq!(options.http_method(), &HttpMethod::Put);
243        assert!(!options.compression_enabled());
244        assert!(options.metadata_dict().is_empty());
245    }
246
247    #[test]
248    fn test_upload_options_default() {
249        let options = UploadOptions::default();
250        assert_eq!(options.http_method(), &HttpMethod::Put);
251        assert!(!options.compression_enabled());
252        assert!(options.metadata_dict().is_empty());
253    }
254
255    #[test]
256    fn test_upload_options_with_method() {
257        let options = UploadOptions::with_method(HttpMethod::Post);
258        assert_eq!(options.http_method(), &HttpMethod::Post);
259        assert!(!options.compression_enabled());
260        assert!(options.metadata_dict().is_empty());
261    }
262
263    #[test]
264    fn test_upload_options_method_setter() {
265        let options = UploadOptions::new().method(HttpMethod::Patch);
266        assert_eq!(options.http_method(), &HttpMethod::Patch);
267    }
268
269    #[test]
270    fn test_upload_options_compression() {
271        let options = UploadOptions::new().compression(true);
272        assert!(options.compression_enabled());
273
274        let options = options.compression(false);
275        assert!(!options.compression_enabled());
276    }
277
278    #[test]
279    fn test_upload_options_metadata() {
280        let options = UploadOptions::new()
281            .metadata("key1", "value1")
282            .metadata("key2", "value2");
283
284        let metadata = options.metadata_dict();
285        assert_eq!(metadata.len(), 2);
286        assert_eq!(
287            metadata.get("KEY1"),
288            Some(&ValueType::String("value1".to_string()))
289        );
290        assert_eq!(
291            metadata.get("KEY2"),
292            Some(&ValueType::String("value2".to_string()))
293        );
294    }
295
296    #[test]
297    fn test_upload_options_for_test() {
298        let options = UploadOptions::for_test();
299        assert_eq!(options.http_method(), &HttpMethod::Put);
300        assert!(!options.compression_enabled());
301        assert!(options.metadata_dict().is_empty());
302    }
303
304    #[test]
305    fn test_upload_options_getters() {
306        let options = UploadOptions::new()
307            .method(HttpMethod::Post)
308            .compression(true)
309            .metadata("test", "value");
310
311        assert_eq!(options.http_method(), &HttpMethod::Post);
312        assert!(options.compression_enabled());
313        assert_eq!(options.metadata_dict().len(), 1);
314    }
315
316    #[test]
317    fn test_upload_options_clone() {
318        let original = UploadOptions::new()
319            .method(HttpMethod::Patch)
320            .compression(true)
321            .metadata("clone", "test");
322
323        let cloned = original.clone();
324        assert_eq!(original.http_method(), cloned.http_method());
325        assert_eq!(original.compression_enabled(), cloned.compression_enabled());
326        assert_eq!(original.metadata_dict(), cloned.metadata_dict());
327    }
328
329    #[test]
330    fn test_upload_options_debug() {
331        let options = UploadOptions::new()
332            .method(HttpMethod::Post)
333            .compression(true)
334            .metadata("debug", "test");
335
336        let debug_str = format!("{options:?}");
337        assert!(debug_str.contains("UploadOptions"));
338        assert!(debug_str.contains("Post"));
339        assert!(debug_str.contains("true"));
340    }
341
342    #[test]
343    fn test_from_usize_value_dict() {
344        let mut metadata = ValueDict::new();
345        metadata.insert("test".to_string(), ValueType::String("value".to_string()));
346
347        let options: UploadOptions = (0, metadata.clone()).into();
348        assert_eq!(options.http_method(), &HttpMethod::Put);
349        assert_eq!(options.metadata_dict(), &metadata);
350
351        let options: UploadOptions = (1, metadata.clone()).into();
352        assert_eq!(options.http_method(), &HttpMethod::Post);
353
354        let options: UploadOptions = (2, metadata.clone()).into();
355        assert_eq!(options.http_method(), &HttpMethod::Patch);
356
357        let options: UploadOptions = (999, metadata.clone()).into();
358        assert_eq!(options.http_method(), &HttpMethod::Put); // 默认值
359    }
360
361    #[test]
362    fn test_from_download_options() {
363        // 注意:这里我们无法创建 DownloadOptions 实例,因为它在另一个模块中
364        // 但我们可以测试 From trait 的存在性
365        let _ = UploadOptions::from(crate::update::DownloadOptions::default());
366    }
367
368    #[test]
369    fn test_upload_options_builder_pattern() {
370        let options = UploadOptions::new()
371            .method(HttpMethod::Post)
372            .compression(true)
373            .metadata("author", "test")
374            .metadata("version", "1.0");
375
376        assert_eq!(options.http_method(), &HttpMethod::Post);
377        assert!(options.compression_enabled());
378        assert_eq!(options.metadata_dict().len(), 2);
379    }
380
381    #[test]
382    fn test_upload_options_chaining() {
383        let base = UploadOptions::new();
384        let options1 = base.clone().method(HttpMethod::Patch);
385        let options2 = base.clone().compression(true);
386        let options3 = base.clone().metadata("key", "value");
387
388        assert_eq!(options1.http_method(), &HttpMethod::Patch);
389        assert!(!options1.compression_enabled());
390
391        assert_eq!(options2.http_method(), &HttpMethod::Put);
392        assert!(options2.compression_enabled());
393
394        assert_eq!(options3.http_method(), &HttpMethod::Put);
395        assert!(!options3.compression_enabled());
396        assert_eq!(options3.metadata_dict().len(), 1);
397    }
398}