• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

tu6ge / oss-rs / 5874371460

pending completion
5874371460

Pull #24

github

tu6ge
chore: add clippy deprecated_semver rule
Pull Request #24: Branch0.12

39 of 39 new or added lines in 4 files covered. (100.0%)

6180 of 6422 relevant lines covered (96.23%)

9.24 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.61
/src/types/core.rs
1
//! 核心功能用到的类型 Query ContentRange 等
2

3
use std::borrow::Cow;
4
use std::collections::HashMap;
5

6
const DELIMITER: &str = "delimiter";
7
const START_AFTER: &str = "start-after";
8
const CONTINUATION_TOKEN: &str = "continuation-token";
9
const MAX_KEYS: &str = "max-keys";
10
const PREFIX: &str = "prefix";
11
const ENCODING_TYPE: &str = "encoding-type";
12
const FETCH_OWNER: &str = "fetch-owner";
13
const DEFAULT_MAX_KEYS: usize = 100;
14

15
//===================================================================================================
16
/// 查询条件
17
///
18
/// ```
19
/// use aliyun_oss_client::types::Query;
20
///
21
/// let query: Query = [("abc", "def")].into_iter().collect();
22
/// assert_eq!(query.len(), 1);
23
///
24
/// let value = query.get("abc");
25
/// assert!(value.is_some());
26
/// let value = value.unwrap();
27
/// assert_eq!(value.as_ref(), "def");
28
///
29
/// let str = query.to_oss_string();
30
/// assert_eq!(str.as_str(), "list-type=2&abc=def");
31
/// let str = query.to_url_query();
32
/// assert_eq!(str.as_str(), "abc=def");
33
/// ```
34
#[derive(Clone, Debug, Default)]
118✔
35
pub struct Query {
36
    inner: HashMap<QueryKey, QueryValue>,
59✔
37
}
38

39
impl AsMut<HashMap<QueryKey, QueryValue>> for Query {
40
    fn as_mut(&mut self) -> &mut HashMap<QueryKey, QueryValue> {
56✔
41
        &mut self.inner
42
    }
56✔
43
}
44

45
impl AsRef<HashMap<QueryKey, QueryValue>> for Query {
46
    fn as_ref(&self) -> &HashMap<QueryKey, QueryValue> {
79✔
47
        &self.inner
48
    }
79✔
49
}
50

51
impl Query {
52
    /// Creates an empty `Query`.
53
    ///
54
    /// The hash map is initially created with a capacity of 0, so it will not allocate until it
55
    /// is first inserted into.
56
    pub fn new() -> Self {
6✔
57
        Self {
6✔
58
            inner: HashMap::new(),
6✔
59
        }
60
    }
6✔
61

62
    /// Creates an empty `Query` with at least the specified capacity.
63
    pub fn with_capacity(capacity: usize) -> Self {
×
64
        Self {
×
65
            inner: HashMap::with_capacity(capacity),
×
66
        }
67
    }
×
68

69
    /// Inserts a key-value pair into the map.
70
    pub fn insert(
5✔
71
        &mut self,
72
        key: impl Into<QueryKey>,
73
        value: impl Into<QueryValue>,
74
    ) -> Option<QueryValue> {
75
        self.as_mut().insert(key.into(), value.into())
5✔
76
    }
5✔
77

78
    /// Returns a reference to the value corresponding to the key.
79
    pub fn get(&self, key: impl Into<QueryKey>) -> Option<&QueryValue> {
39✔
80
        self.as_ref().get(&key.into())
39✔
81
    }
39✔
82

83
    /// Returns the number of elements in the map.
84
    pub fn len(&self) -> usize {
1✔
85
        self.as_ref().len()
1✔
86
    }
1✔
87

88
    /// Returns `true` if the map contains no elements.
89
    pub fn is_empty(&self) -> bool {
14✔
90
        self.as_ref().is_empty()
14✔
91
    }
14✔
92

93
    /// Removes a key from the map, returning the value at the key if the key
94
    /// was previously in the map.
95
    pub fn remove(&mut self, key: impl Into<QueryKey>) -> Option<QueryValue> {
96
        self.as_mut().remove(&key.into())
97
    }
98

99
    /// 将查询参数拼成 aliyun 接口需要的格式
100
    pub fn to_oss_string(&self) -> String {
12✔
101
        const LIST_TYPE2: &str = "list-type=2";
102
        let mut query_str = String::from(LIST_TYPE2);
12✔
103
        for (key, value) in self.as_ref().iter() {
22✔
104
            query_str += "&";
10✔
105
            query_str += key.as_ref();
10✔
106
            query_str += "=";
10✔
107
            query_str += value.as_ref();
10✔
108
        }
109
        query_str
110
    }
12✔
111

112
    /// 转化成 url 参数的形式
113
    /// a=foo&b=bar
114
    pub fn to_url_query(&self) -> String {
2✔
115
        self.as_ref()
2✔
116
            .iter()
117
            .map(|(k, v)| {
4✔
118
                let mut res = String::with_capacity(k.as_ref().len() + v.as_ref().len() + 1);
4✔
119
                res.push_str(k.as_ref());
2✔
120
                res.push('=');
2✔
121
                res.push_str(v.as_ref());
2✔
122
                res
123
            })
2✔
124
            .collect::<Vec<_>>()
125
            .join("&")
126
    }
2✔
127

128
    pub(crate) fn get_max_keys(&self) -> usize {
10✔
129
        match self.get(QueryKey::MAX_KEYS) {
10✔
130
            Some(capacity) => capacity.try_into().unwrap_or(DEFAULT_MAX_KEYS),
8✔
131
            None => DEFAULT_MAX_KEYS,
2✔
132
        }
133
    }
10✔
134
}
135

136
#[cfg(test)]
137
mod test_query {
138
    use super::*;
139
    #[test]
140
    fn get_max_keys() {
2✔
141
        let query = Query::new();
1✔
142
        assert_eq!(query.get_max_keys(), 100);
1✔
143

144
        let mut query = Query::new();
1✔
145
        query.insert(QueryKey::MAX_KEYS, "10");
1✔
146
        assert_eq!(query.get_max_keys(), 10);
1✔
147

148
        let mut query = Query::new();
1✔
149
        query.insert(QueryKey::MAX_KEYS, "str");
1✔
150
        assert_eq!(query.get_max_keys(), 100);
1✔
151
    }
2✔
152
}
153

154
impl Index<QueryKey> for Query {
155
    type Output = QueryValue;
156

157
    fn index(&self, index: QueryKey) -> &Self::Output {
×
158
        self.get(index).expect("no found query key")
×
159
    }
×
160
}
161

162
impl IntoIterator for Query {
163
    type Item = (QueryKey, QueryValue);
164
    type IntoIter = std::vec::IntoIter<Self::Item>;
165
    /// # 使用 Vec 转 Query
166
    fn into_iter(self) -> Self::IntoIter {
2✔
167
        self.inner.into_iter().collect::<Vec<_>>().into_iter()
2✔
168
    }
2✔
169
}
170

171
impl FromIterator<(QueryKey, QueryValue)> for Query {
172
    fn from_iter<I>(iter: I) -> Self
32✔
173
    where
174
        I: IntoIterator<Item = (QueryKey, QueryValue)>,
175
    {
176
        let mut map = Query::default();
32✔
177
        map.as_mut().extend(iter);
32✔
178
        map
179
    }
32✔
180
}
181

182
impl<'a> FromIterator<(&'a str, &'a str)> for Query {
183
    /// 转化例子
184
    /// ```
185
    /// # use aliyun_oss_client::Query;
186
    /// # use aliyun_oss_client::QueryKey;
187
    /// let query: Query = [("max-keys", "123")].into_iter().collect();
188
    /// assert_eq!(query.get(QueryKey::MaxKeys), Some(&123u8.into()));
189
    /// assert_eq!(query.get(QueryKey::MaxKeys), Some(&123u16.into()));
190
    /// ```
191
    fn from_iter<I>(iter: I) -> Self
1✔
192
    where
193
        I: IntoIterator<Item = (&'a str, &'a str)>,
194
    {
195
        let inner = iter.into_iter().map(|(k, v)| {
2✔
196
            (
1✔
197
                k.parse().expect("invalid QueryKey"),
1✔
198
                v.parse().expect("invalid QueryValue"),
1✔
199
            )
200
        });
1✔
201

202
        let mut map = Query::default();
1✔
203
        map.as_mut().extend(inner);
1✔
204
        map
205
    }
1✔
206
}
207

208
impl<'a> FromIterator<(Cow<'a, str>, Cow<'a, str>)> for Query {
209
    /// 转化例子
210
    /// ```
211
    /// # use aliyun_oss_client::Query;
212
    /// # use aliyun_oss_client::QueryKey;
213
    /// let query: Query = [("max-keys", "123")].into_iter().collect();
214
    /// assert_eq!(query.get(QueryKey::MaxKeys), Some(&123u8.into()));
215
    /// assert_eq!(query.get(QueryKey::MaxKeys), Some(&123u16.into()));
216
    /// ```
217
    fn from_iter<I>(iter: I) -> Self
218
    where
219
        I: IntoIterator<Item = (Cow<'a, str>, Cow<'a, str>)>,
220
    {
221
        let inner = iter.into_iter().map(|(k, v)| {
222
            (
223
                k.as_ref().parse().expect("invalid QueryKey"),
224
                v.as_ref().parse().expect("invalid QueryValue"),
225
            )
226
        });
227

228
        let mut map = Query::default();
229
        map.as_mut().extend(inner);
230
        map
231
    }
232
}
233

234
impl<'a> FromIterator<(&'a str, u8)> for Query {
235
    /// 转化例子
236
    /// ```
237
    /// # use aliyun_oss_client::Query;
238
    /// # use aliyun_oss_client::QueryKey;
239
    /// let query = Query::from_iter([("max-keys", 123u8)]);
240
    /// assert_eq!(query.get(QueryKey::MaxKeys), Some(&123u8.into()));
241
    /// assert_eq!(query.get(QueryKey::MaxKeys), Some(&123u16.into()));
242
    /// ```
243
    fn from_iter<I>(iter: I) -> Self
244
    where
245
        I: IntoIterator<Item = (&'a str, u8)>,
246
    {
247
        let inner = iter
248
            .into_iter()
249
            .map(|(k, v)| (k.parse().expect("invalid QueryKey"), v.into()));
250

251
        let mut map = Query::default();
252
        map.as_mut().extend(inner);
253
        map
254
    }
255
}
256

257
impl<'a> FromIterator<(&'a str, u16)> for Query {
258
    /// 转化例子
259
    /// ```
260
    /// # use aliyun_oss_client::Query;
261
    /// # use aliyun_oss_client::QueryKey;
262
    /// let query = Query::from_iter([("max-keys", 123u16)]);
263
    /// assert_eq!(query.get(QueryKey::MaxKeys), Some(&123u8.into()));
264
    /// assert_eq!(query.get(QueryKey::MaxKeys), Some(&123u16.into()));
265
    /// ```
266
    fn from_iter<I>(iter: I) -> Self
267
    where
268
        I: IntoIterator<Item = (&'a str, u16)>,
269
    {
270
        let inner = iter
271
            .into_iter()
272
            .map(|(k, v)| (k.parse().expect("invalid QueryKey"), v.into()));
273

274
        let mut map = Query::default();
275
        map.as_mut().extend(inner);
276
        map
277
    }
278
}
279

280
impl<'a> FromIterator<(QueryKey, &'a str)> for Query {
281
    /// 转化例子
282
    /// ```
283
    /// # use aliyun_oss_client::Query;
284
    /// # use aliyun_oss_client::QueryKey;
285
    /// let query = Query::from_iter([(QueryKey::MaxKeys, "123")]);
286
    /// assert_eq!(query.get(QueryKey::MaxKeys), Some(&123u8.into()));
287
    /// assert_eq!(query.get(QueryKey::MaxKeys), Some(&123u16.into()));
288
    /// ```
289
    fn from_iter<I>(iter: I) -> Self
290
    where
291
        I: IntoIterator<Item = (QueryKey, &'a str)>,
292
    {
293
        let inner = iter
294
            .into_iter()
295
            .map(|(k, v)| (k, v.parse().expect("invalid QueryValue")));
296

297
        let mut map = Query::default();
298
        map.as_mut().extend(inner);
299
        map
300
    }
301
}
302

303
macro_rules! impl_from_iter {
304
    ($key:ty, $val:ty, $convert:expr) => {
305
        impl FromIterator<($key, $val)> for Query {
306
            fn from_iter<I>(iter: I) -> Self
7✔
307
            where
308
                I: IntoIterator<Item = ($key, $val)>,
309
            {
310
                let inner = iter.into_iter().map($convert);
7✔
311

312
                let mut map = Query::default();
7✔
313
                map.as_mut().extend(inner);
7✔
314
                map
315
            }
7✔
316
        }
317
    };
318
}
319

320
impl_from_iter!(QueryKey, u8, |(k, v)| (k, v.into()));
1✔
321
impl_from_iter!(QueryKey, u16, |(k, v)| (k, v.into()));
6✔
322

323
#[cfg(test)]
324
mod tests_query_from_iter {
325
    use super::*;
326
    #[test]
327
    fn test() {
2✔
328
        let query = Query::from_iter([(QueryKey::MAX_KEYS, 123u8)]);
1✔
329
        assert_eq!(query.get(QueryKey::MAX_KEYS), Some(&123u8.into()));
1✔
330
        assert_eq!(query.get(QueryKey::MAX_KEYS), Some(&123u16.into()));
1✔
331

332
        let query = Query::from_iter([(QueryKey::MAX_KEYS, 123u16)]);
1✔
333
        assert_eq!(query.get(QueryKey::MAX_KEYS), Some(&123u8.into()));
1✔
334
        assert_eq!(query.get(QueryKey::MAX_KEYS), Some(&123u16.into()));
1✔
335
    }
2✔
336
}
337

338
impl PartialEq<Query> for Query {
339
    fn eq(&self, other: &Query) -> bool {
5✔
340
        self.as_ref() == other.as_ref()
5✔
341
    }
5✔
342
}
343

344
/// 为 Url 拼接 [`Query`] 数据
345
/// [`Query`]: crate::types::Query
346
pub trait SetOssQuery {
347
    /// 给 Url 结构体增加 `set_search_query` 方法
348
    fn set_oss_query(&mut self, query: &Query);
349
}
350

351
impl SetOssQuery for Url {
352
    /// 将查询参数拼接到 API 的 Url 上
353
    ///
354
    /// # 例子
355
    /// ```
356
    /// use aliyun_oss_client::types::Query;
357
    /// use aliyun_oss_client::types::SetOssQuery;
358
    /// use reqwest::Url;
359
    ///
360
    /// let query = Query::from_iter([("abc", "def")]);
361
    /// let mut url = Url::parse("https://exapmle.com").unwrap();
362
    /// url.set_oss_query(&query);
363
    /// assert_eq!(url.as_str(), "https://exapmle.com/?list-type=2&abc=def");
364
    /// assert_eq!(url.query(), Some("list-type=2&abc=def"));
365
    /// ```
366
    fn set_oss_query(&mut self, query: &Query) {
11✔
367
        self.set_query(Some(&query.to_oss_string()));
11✔
368
    }
11✔
369
}
370

371
/// 查询条件的键
372
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
230✔
373
#[non_exhaustive]
374
pub struct InnerQueryKey<'a> {
375
    kind: QueryKeyEnum<'a>,
115✔
376
}
377

378
// TODO
379
#[allow(non_upper_case_globals)]
380
impl InnerQueryKey<'_> {
381
    /// 对Object名字进行分组的字符。所有Object名字包含指定的前缀,第一次出现delimiter字符之间的Object作为一组元素(即CommonPrefixes)
382
    /// 示例值 `/`
383
    pub const DELIMITER: Self = Self {
384
        kind: QueryKeyEnum::Delimiter,
385
    };
386
    /// TODO
387
    #[deprecated(since = "0.13.0", note = "replace with QueryKey::DELIMITER")]
388
    pub const Delimiter: Self = Self {
389
        kind: QueryKeyEnum::Delimiter,
390
    };
391

392
    /// 设定从start-after之后按字母排序开始返回Object。
393
    /// start-after用来实现分页显示效果,参数的长度必须小于1024字节。
394
    /// 做条件查询时,即使start-after在列表中不存在,也会从符合start-after字母排序的下一个开始打印。
395
    pub const START_AFTER: Self = Self {
396
        kind: QueryKeyEnum::StartAfter,
397
    };
398
    /// TODO
399
    #[deprecated(since = "0.13.0", note = "replace with QueryKey::START_AFTER")]
400
    pub const StartAfter: Self = Self {
401
        kind: QueryKeyEnum::StartAfter,
402
    };
403

404
    /// 指定List操作需要从此token开始。您可从ListObjectsV2(GetBucketV2)结果中的NextContinuationToken获取此token。
405
    /// 用于分页,返回下一页的数据
406
    pub const CONTINUATION_TOKEN: Self = Self {
407
        kind: QueryKeyEnum::ContinuationToken,
408
    };
409
    /// TODO
410
    #[deprecated(since = "0.13.0", note = "replace with QueryKey::CONTINUATION_TOKEN")]
411
    pub const ContinuationToken: Self = Self {
412
        kind: QueryKeyEnum::ContinuationToken,
413
    };
414

415
    /// 指定返回Object的最大数。
416
    /// 取值:大于0小于等于1000
417
    pub const MAX_KEYS: Self = Self {
418
        kind: QueryKeyEnum::MaxKeys,
419
    };
420
    /// TODO
421
    #[deprecated(since = "0.13.0", note = "replace with QueryKey::MAX_KEYS")]
422
    pub const MaxKeys: Self = Self {
423
        kind: QueryKeyEnum::MaxKeys,
424
    };
425

426
    /// # 限定返回文件的Key必须以prefix作为前缀。
427
    /// 如果把prefix设为某个文件夹名,则列举以此prefix开头的文件,即该文件夹下递归的所有文件和子文件夹。
428
    ///
429
    /// 在设置prefix的基础上,将delimiter设置为正斜线(/)时,返回值就只列举该文件夹下的文件,文件夹下的子文件夹名返回在CommonPrefixes中,
430
    /// 子文件夹下递归的所有文件和文件夹不显示。
431
    ///
432
    /// 例如,一个Bucket中有三个Object,分别为fun/test.jpg、fun/movie/001.avi和fun/movie/007.avi。如果设定prefix为fun/,
433
    /// 则返回三个Object;如果在prefix设置为fun/的基础上,将delimiter设置为正斜线(/),则返回fun/test.jpg和fun/movie/。
434
    /// ## 要求
435
    /// - 参数的长度必须小于1024字节。
436
    /// - 设置prefix参数时,不能以正斜线(/)开头。如果prefix参数置空,则默认列举Bucket内的所有Object。
437
    /// - 使用prefix查询时,返回的Key中仍会包含prefix。
438
    pub const PREFIX: Self = Self {
439
        kind: QueryKeyEnum::Prefix,
440
    };
441
    /// TODO
442
    #[deprecated(since = "0.13.0", note = "replace with QueryKey::PREFIX")]
443
    pub const Prefix: Self = Self {
444
        kind: QueryKeyEnum::Prefix,
445
    };
446

447
    /// 对返回的内容进行编码并指定编码的类型。
448
    pub const ENCODING_TYPE: Self = Self {
449
        kind: QueryKeyEnum::EncodingType,
450
    };
451
    /// TODO
452
    #[deprecated(since = "0.13.0", note = "replace with EndPoint::ENCODING_TYPE")]
453
    pub const EncodingType: Self = Self {
454
        kind: QueryKeyEnum::EncodingType,
455
    };
456

457
    /// 指定是否在返回结果中包含owner信息。
458
    pub const FETCH_OWNER: Self = Self {
459
        kind: QueryKeyEnum::FetchOwner,
460
    };
461
    /// TODO
462
    #[deprecated(since = "0.13.0", note = "replace with EndPoint::FETCH_OWNER")]
463
    pub const FetchOwner: Self = Self {
464
        kind: QueryKeyEnum::FetchOwner,
465
    };
466
}
467

468
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
134✔
469
#[non_exhaustive]
470
enum QueryKeyEnum<'a> {
471
    /// 对Object名字进行分组的字符。所有Object名字包含指定的前缀,第一次出现delimiter字符之间的Object作为一组元素(即CommonPrefixes)
472
    /// 示例值 `/`
473
    Delimiter,
474

475
    /// 设定从start-after之后按字母排序开始返回Object。
476
    /// start-after用来实现分页显示效果,参数的长度必须小于1024字节。
477
    /// 做条件查询时,即使start-after在列表中不存在,也会从符合start-after字母排序的下一个开始打印。
478
    StartAfter,
479

480
    /// 指定List操作需要从此token开始。您可从ListObjectsV2(GetBucketV2)结果中的NextContinuationToken获取此token。
481
    /// 用于分页,返回下一页的数据
482
    ContinuationToken,
483

484
    /// 指定返回Object的最大数。
485
    /// 取值:大于0小于等于1000
486
    MaxKeys,
487

488
    /// # 限定返回文件的Key必须以prefix作为前缀。
489
    /// 如果把prefix设为某个文件夹名,则列举以此prefix开头的文件,即该文件夹下递归的所有文件和子文件夹。
490
    ///
491
    /// 在设置prefix的基础上,将delimiter设置为正斜线(/)时,返回值就只列举该文件夹下的文件,文件夹下的子文件夹名返回在CommonPrefixes中,
492
    /// 子文件夹下递归的所有文件和文件夹不显示。
493
    ///
494
    /// 例如,一个Bucket中有三个Object,分别为fun/test.jpg、fun/movie/001.avi和fun/movie/007.avi。如果设定prefix为fun/,
495
    /// 则返回三个Object;如果在prefix设置为fun/的基础上,将delimiter设置为正斜线(/),则返回fun/test.jpg和fun/movie/。
496
    /// ## 要求
497
    /// - 参数的长度必须小于1024字节。
498
    /// - 设置prefix参数时,不能以正斜线(/)开头。如果prefix参数置空,则默认列举Bucket内的所有Object。
499
    /// - 使用prefix查询时,返回的Key中仍会包含prefix。
500
    Prefix,
501

502
    /// 对返回的内容进行编码并指定编码的类型。
503
    EncodingType,
504

505
    /// 指定是否在返回结果中包含owner信息。
506
    FetchOwner,
507

508
    /// 自定义
509
    Custom(Cow<'a, str>),
21✔
510
}
511

512
/// 查询条件的键
513
pub type QueryKey = InnerQueryKey<'static>;
514

515
impl AsRef<str> for InnerQueryKey<'_> {
516
    /// ```
517
    /// # use aliyun_oss_client::QueryKey;
518
    /// # use std::borrow::Cow;
519
    /// assert_eq!(QueryKey::Delimiter.as_ref(), "delimiter");
520
    /// assert_eq!(QueryKey::StartAfter.as_ref(), "start-after");
521
    /// assert_eq!(QueryKey::ContinuationToken.as_ref(), "continuation-token");
522
    /// assert_eq!(QueryKey::MaxKeys.as_ref(), "max-keys");
523
    /// assert_eq!(QueryKey::Prefix.as_ref(), "prefix");
524
    /// assert_eq!(QueryKey::EncodingType.as_ref(), "encoding-type");
525
    /// assert_eq!(QueryKey::new("abc").as_ref(), "abc");
526
    /// ```
527
    fn as_ref(&self) -> &str {
22✔
528
        use QueryKeyEnum::*;
529

530
        match &self.kind {
22✔
531
            Delimiter => "delimiter",
2✔
532
            StartAfter => "start-after",
1✔
533
            ContinuationToken => "continuation-token",
1✔
534
            MaxKeys => "max-keys",
9✔
535
            Prefix => "prefix",
1✔
536
            EncodingType => "encoding-type",
1✔
537
            // TODO
538
            FetchOwner => unimplemented!("parse xml not support fetch owner"),
×
539
            Custom(str) => str.as_ref(),
7✔
540
        }
541
    }
22✔
542
}
543

544
impl Display for InnerQueryKey<'_> {
545
    /// ```
546
    /// # use aliyun_oss_client::QueryKey;
547
    /// assert_eq!(format!("{}", QueryKey::Delimiter), "delimiter");
548
    /// ```
549
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1✔
550
        write!(f, "{}", self.as_ref())
1✔
551
    }
1✔
552
}
553

554
impl From<String> for InnerQueryKey<'_> {
555
    fn from(s: String) -> Self {
×
556
        Self::new(s)
×
557
    }
×
558
}
559
impl<'a: 'b, 'b> From<&'a str> for InnerQueryKey<'b> {
560
    fn from(date: &'a str) -> Self {
19✔
561
        Self::new(date)
19✔
562
    }
19✔
563
}
564

565
impl FromStr for QueryKey {
566
    type Err = InvalidQueryKey;
567
    /// 示例
568
    /// ```
569
    /// # use aliyun_oss_client::types::QueryKey;
570
    /// let value: QueryKey = "abc".into();
571
    /// assert!(value == QueryKey::from_static("abc"));
572
    /// ```
573
    fn from_str(s: &str) -> Result<Self, InvalidQueryKey> {
13✔
574
        Ok(Self::from_static(s))
13✔
575
    }
13✔
576
}
577

578
/// 异常的查询条件键
579
#[derive(Debug)]
×
580
pub struct InvalidQueryKey {
581
    _priv: (),
×
582
}
583

584
impl Error for InvalidQueryKey {}
585

586
impl Display for InvalidQueryKey {
587
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
×
588
        write!(f, "invalid query key")
×
589
    }
×
590
}
591

592
impl<'a> InnerQueryKey<'a> {
593
    /// # Examples
594
    /// ```
595
    /// # use aliyun_oss_client::QueryKey;
596
    /// let key = QueryKey::new("delimiter");
597
    /// assert!(key == QueryKey::Delimiter);
598
    /// assert!(QueryKey::new("start-after") == QueryKey::StartAfter);
599
    /// assert!(QueryKey::new("continuation-token") == QueryKey::ContinuationToken);
600
    /// assert!(QueryKey::new("max-keys") == QueryKey::MaxKeys);
601
    /// assert!(QueryKey::new("prefix") == QueryKey::Prefix);
602
    /// assert!(QueryKey::new("encoding-type") == QueryKey::EncodingType);
603
    /// ```
604
    /// *`fetch-owner` 功能未实现,特殊说明*
605
    pub fn new(val: impl Into<Cow<'a, str>>) -> Self {
28✔
606
        use QueryKeyEnum::*;
607

608
        let val = val.into();
28✔
609
        let kind = if val.contains(DELIMITER) {
28✔
610
            Delimiter
1✔
611
        } else if val.contains(START_AFTER) {
27✔
612
            StartAfter
1✔
613
        } else if val.contains(CONTINUATION_TOKEN) {
26✔
614
            ContinuationToken
3✔
615
        } else if val.contains(MAX_KEYS) {
23✔
616
            MaxKeys
6✔
617
        } else if val.contains(PREFIX) {
17✔
618
            Prefix
1✔
619
        } else if val.contains(ENCODING_TYPE) {
30✔
620
            EncodingType
1✔
621
        } else if val.contains(FETCH_OWNER) {
15✔
622
            unimplemented!("parse xml not support fetch owner");
1✔
623
        } else {
624
            Custom(val)
14✔
625
        };
626
        Self { kind }
27✔
627
    }
27✔
628

629
    /// # Examples
630
    /// ```
631
    /// # use aliyun_oss_client::QueryKey;
632
    /// let key = QueryKey::from_static("delimiter");
633
    /// assert!(key == QueryKey::Delimiter);
634
    /// assert!(QueryKey::from_static("start-after") == QueryKey::StartAfter);
635
    /// assert!(QueryKey::from_static("continuation-token") == QueryKey::ContinuationToken);
636
    /// assert!(QueryKey::from_static("max-keys") == QueryKey::MaxKeys);
637
    /// assert!(QueryKey::from_static("prefix") == QueryKey::Prefix);
638
    /// assert!(QueryKey::from_static("encoding-type") == QueryKey::EncodingType);
639
    /// ```
640
    /// *`fetch-owner` 功能未实现,特殊说明*
641
    pub fn from_static(val: &str) -> Self {
21✔
642
        use QueryKeyEnum::*;
643

644
        let kind = if val.contains(DELIMITER) {
21✔
645
            Delimiter
1✔
646
        } else if val.contains(START_AFTER) {
20✔
647
            StartAfter
1✔
648
        } else if val.contains(CONTINUATION_TOKEN) {
19✔
649
            ContinuationToken
1✔
650
        } else if val.contains(MAX_KEYS) {
18✔
651
            MaxKeys
8✔
652
        } else if val.contains(PREFIX) {
10✔
653
            Prefix
1✔
654
        } else if val.contains(ENCODING_TYPE) {
17✔
655
            EncodingType
1✔
656
        } else if val.contains(FETCH_OWNER) {
8✔
657
            unimplemented!("parse xml not support fetch owner");
×
658
        } else {
659
            Custom(Cow::Owned(val.to_owned()))
8✔
660
        };
661
        Self { kind }
21✔
662
    }
21✔
663
}
664

665
#[cfg(test)]
666
mod test_query_key {
667
    use super::*;
668

669
    #[test]
670
    #[should_panic]
671
    fn test_fetch_owner() {
2✔
672
        QueryKey::new("fetch-owner");
1✔
673
    }
2✔
674

675
    #[test]
676
    fn test_custom() {
2✔
677
        let key = QueryKey::new("abc");
1✔
678
        assert!(matches!(key.kind, QueryKeyEnum::Custom(_)));
1✔
679
    }
2✔
680

681
    #[test]
682
    fn test_into_iter() {
2✔
683
        let query = Query::from_iter(vec![("foo", "bar")]);
1✔
684
        let list: Vec<_> = query.into_iter().collect();
1✔
685
        assert_eq!(list.len(), 1);
1✔
686
        assert!(matches!(&list[0].0.kind, &QueryKeyEnum::Custom(_)));
1✔
687
        let value: QueryValue = "bar".parse().unwrap();
1✔
688
        assert_eq!(list[0].1, value);
1✔
689
    }
2✔
690

691
    #[test]
692
    fn test_from_static() {
2✔
693
        let key = QueryKey::from_static("abc");
1✔
694
        assert!(matches!(key.kind, QueryKeyEnum::Custom(_)));
1✔
695
    }
2✔
696
}
697

698
/// 查询条件的值
699
#[derive(Clone, Debug, PartialEq, Eq, Default)]
32✔
700
pub struct InnerQueryValue<'a>(Cow<'a, str>);
16✔
701
/// 查询条件的值
702
pub type QueryValue = InnerQueryValue<'static>;
703

704
impl AsRef<str> for InnerQueryValue<'_> {
705
    fn as_ref(&self) -> &str {
16✔
706
        &self.0
16✔
707
    }
16✔
708
}
709

710
impl Display for InnerQueryValue<'_> {
711
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
×
712
        write!(f, "{}", self.0)
×
713
    }
×
714
}
715

716
impl From<String> for InnerQueryValue<'_> {
717
    fn from(s: String) -> Self {
1✔
718
        Self(Cow::Owned(s))
1✔
719
    }
1✔
720
}
721
impl<'a: 'b, 'b> From<&'a str> for InnerQueryValue<'b> {
722
    fn from(date: &'a str) -> Self {
16✔
723
        Self::new(date)
16✔
724
    }
16✔
725
}
726

727
impl PartialEq<&str> for InnerQueryValue<'_> {
728
    #[inline]
729
    fn eq(&self, other: &&str) -> bool {
×
730
        &self.0 == other
×
731
    }
×
732
}
733

734
impl From<u8> for InnerQueryValue<'_> {
735
    /// 数字转 Query 值
736
    ///
737
    /// ```
738
    /// # use aliyun_oss_client::Query;
739
    /// # use aliyun_oss_client::QueryKey;
740
    /// let query = Query::from_iter([("max_keys", 100u8)]);
741
    /// let query = Query::from_iter([(QueryKey::MaxKeys, 100u8)]);
742
    /// ```
743
    fn from(num: u8) -> Self {
12✔
744
        Self(Cow::Owned(num.to_string()))
12✔
745
    }
12✔
746
}
747

748
impl PartialEq<u8> for InnerQueryValue<'_> {
749
    #[inline]
750
    fn eq(&self, other: &u8) -> bool {
×
751
        self.to_string() == other.to_string()
×
752
    }
×
753
}
754

755
impl From<u16> for InnerQueryValue<'_> {
756
    /// 数字转 Query 值
757
    ///
758
    /// ```
759
    /// use aliyun_oss_client::Query;
760
    /// let query = Query::from_iter([("max_keys", 100u16)]);
761
    /// ```
762
    fn from(num: u16) -> Self {
15✔
763
        Self(Cow::Owned(num.to_string()))
15✔
764
    }
15✔
765
}
766

767
impl PartialEq<u16> for InnerQueryValue<'_> {
768
    #[inline]
769
    fn eq(&self, other: &u16) -> bool {
×
770
        self.to_string() == other.to_string()
×
771
    }
×
772
}
773

774
impl From<bool> for QueryValue {
775
    /// bool 转 Query 值
776
    ///
777
    /// ```
778
    /// use aliyun_oss_client::Query;
779
    /// let query = Query::from_iter([("abc", "false")]);
780
    /// ```
781
    fn from(b: bool) -> Self {
×
782
        if b {
×
783
            Self::from_static("true")
×
784
        } else {
785
            Self::from_static("false")
×
786
        }
787
    }
×
788
}
789

790
impl FromStr for InnerQueryValue<'_> {
791
    type Err = InvalidQueryValue;
792
    /// 示例
793
    /// ```
794
    /// # use aliyun_oss_client::types::QueryValue;
795
    /// let value: QueryValue = "abc".parse().unwrap();
796
    /// assert!(value == "abc");
797
    /// ```
798
    fn from_str(s: &str) -> Result<Self, Self::Err> {
12✔
799
        Ok(Self::from_static2(s))
12✔
800
    }
12✔
801
}
802

803
impl TryFrom<&InnerQueryValue<'_>> for usize {
804
    type Error = ParseIntError;
805
    fn try_from(value: &InnerQueryValue<'_>) -> Result<Self, Self::Error> {
8✔
806
        value.0.parse()
8✔
807
    }
8✔
808
}
809

810
/// 异常的查询值
811
#[derive(Debug)]
×
812
pub struct InvalidQueryValue {
813
    _priv: (),
×
814
}
815

816
impl Error for InvalidQueryValue {}
817

818
impl Display for InvalidQueryValue {
819
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
×
820
        write!(f, "invalid query value")
×
821
    }
×
822
}
823

824
impl<'a> InnerQueryValue<'a> {
825
    /// Creates a new `QueryValue` from the given string.
826
    pub fn new(val: impl Into<Cow<'a, str>>) -> Self {
16✔
827
        Self(val.into())
16✔
828
    }
16✔
829

830
    /// Const function that creates a new `QueryValue` from a static str.
831
    pub const fn from_static(val: &'static str) -> Self {
2✔
832
        Self(Cow::Borrowed(val))
2✔
833
    }
2✔
834

835
    /// Const function that creates a new `QueryValue` from a static str.
836
    pub fn from_static2(val: &str) -> Self {
12✔
837
        Self(Cow::Owned(val.to_owned()))
12✔
838
    }
12✔
839
}
840

841
use std::error::Error;
842
use std::fmt::{Display, Formatter};
843
use std::num::ParseIntError;
844
use std::ops::{Index, Range, RangeFrom, RangeFull, RangeTo};
845
use std::str::FromStr;
846

847
use http::HeaderValue;
848
use reqwest::Url;
849

850
/// 用于指定返回内容的区域的 type
851
pub struct ContentRange<Num> {
852
    start: Option<Num>,
853
    end: Option<Num>,
854
}
855

856
unsafe impl<Num: Send> Send for ContentRange<Num> {}
857
unsafe impl<Num: Sync> Sync for ContentRange<Num> {}
858

859
impl<Num> From<Range<Num>> for ContentRange<Num> {
860
    fn from(r: Range<Num>) -> Self {
3✔
861
        Self {
3✔
862
            start: Some(r.start),
3✔
863
            end: Some(r.end),
3✔
864
        }
865
    }
3✔
866
}
867

868
impl From<RangeFull> for ContentRange<u32> {
869
    fn from(_: RangeFull) -> Self {
4✔
870
        Self {
4✔
871
            start: None,
4✔
872
            end: None,
4✔
873
        }
874
    }
4✔
875
}
876

877
impl<Num> From<RangeFrom<Num>> for ContentRange<Num> {
878
    fn from(f: RangeFrom<Num>) -> Self {
3✔
879
        Self {
3✔
880
            start: Some(f.start),
3✔
881
            end: None,
3✔
882
        }
883
    }
3✔
884
}
885

886
impl<Num> From<RangeTo<Num>> for ContentRange<Num> {
887
    fn from(t: RangeTo<Num>) -> Self {
3✔
888
        Self {
3✔
889
            start: None,
3✔
890
            end: Some(t.end),
3✔
891
        }
892
    }
3✔
893
}
894

895
macro_rules! generate_range {
896
    ($($t:ty)*) => ($(
897
        impl From<ContentRange<$t>> for HeaderValue {
898
            /// # 转化成 OSS 需要的格式
899
            /// @link [OSS 文档](https://help.aliyun.com/document_detail/31980.html)
900
            fn from(con: ContentRange<$t>) -> HeaderValue {
16✔
901
                let string = match (con.start, con.end) {
16✔
902
                    (Some(ref start), Some(ref end)) => format!("bytes={}-{}", start, end),
4✔
903
                    (Some(ref start), None) => format!("bytes={}-", start),
4✔
904
                    (None, Some(ref end)) => format!("bytes=0-{}", end),
4✔
905
                    (None, None) => "bytes=0-".to_string(),
4✔
906
                };
907

908
                HeaderValue::from_str(&string).unwrap_or_else(|_| {
16✔
909
                    panic!(
×
910
                        "content-range into header-value failed, content-range is : {}",
911
                        string
912
                    )
913
                })
914
            }
16✔
915
        }
916
    )*)
917
}
918

919
generate_range!(u8 u16 u32 u64 u128 i8 i16 i32 i64 i128);
920

921
#[cfg(test)]
922
mod test_range {
923
    #[test]
924
    fn test() {
2✔
925
        use super::ContentRange;
926
        use reqwest::header::HeaderValue;
927
        fn abc<R: Into<ContentRange<u32>>>(range: R) -> HeaderValue {
4✔
928
            range.into().into()
4✔
929
        }
4✔
930

931
        assert_eq!(abc(..), HeaderValue::from_str("bytes=0-").unwrap());
1✔
932
        assert_eq!(abc(1..), HeaderValue::from_str("bytes=1-").unwrap());
1✔
933
        assert_eq!(abc(10..20), HeaderValue::from_str("bytes=10-20").unwrap());
1✔
934
        assert_eq!(abc(..20), HeaderValue::from_str("bytes=0-20").unwrap());
1✔
935
    }
2✔
936
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc