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

tu6ge / oss-rs / 5863951548

pending completion
5863951548

Pull #24

github

tu6ge
feat(client): Reduce constraints on generic param
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

91.72
/src/types/object.rs
1
//! object 相关的类型
33✔
2

3
use url::Url;
4

5
use std::{
6
    borrow::Cow,
7
    convert::Infallible,
8
    fmt::{self, Debug, Display},
9
    ops::{Add, AddAssign},
10
    path::Path,
11
    str::FromStr,
12
};
13

14
use crate::{BucketName, EndPoint};
15

16
#[cfg(test)]
17
mod test;
18

19
#[cfg(feature = "core")]
20
pub mod base;
21
#[cfg(feature = "core")]
22
pub use base::{InvalidObjectBase, ObjectBase};
23

24
/// OSS Object 存储对象的路径
25
/// 不带前缀 `/`
26
#[derive(Debug, Clone, PartialEq, Eq)]
36✔
27
pub struct ObjectPathInner<'a>(Cow<'a, str>);
18✔
28

29
/// OSS Object 存储对象的路径
30
/// 不带前缀 `/`
31
pub type ObjectPath = ObjectPathInner<'static>;
32

33
impl AsRef<str> for ObjectPathInner<'_> {
34
    fn as_ref(&self) -> &str {
106✔
35
        &self.0
106✔
36
    }
106✔
37
}
38

39
impl fmt::Display for ObjectPathInner<'_> {
40
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1✔
41
        write!(f, "{}", self.0)
1✔
42
    }
1✔
43
}
44

45
impl Default for ObjectPathInner<'_> {
46
    /// 默认值
47
    /// ```
48
    /// # use aliyun_oss_client::types::object::ObjectPath;
49
    /// let path = ObjectPath::default();
50
    /// assert!(path == "");
51
    /// ```
52
    fn default() -> Self {
30✔
53
        Self(Cow::Borrowed(""))
30✔
54
    }
30✔
55
}
56

57
impl PartialEq<&str> for ObjectPathInner<'_> {
58
    /// 相等比较
59
    /// ```
60
    /// # use aliyun_oss_client::types::object::ObjectPath;
61
    /// let path = ObjectPath::new("abc").unwrap();
62
    /// assert!(path == "abc");
63
    /// ```
64
    #[inline]
65
    fn eq(&self, other: &&str) -> bool {
5✔
66
        &self.0 == other
5✔
67
    }
5✔
68
}
69

70
impl PartialEq<ObjectPathInner<'_>> for &str {
71
    /// 相等比较
72
    /// ```
73
    /// # use aliyun_oss_client::types::object::ObjectPath;
74
    /// let path = ObjectPath::new("abc").unwrap();
75
    /// assert!("abc" == path);
76
    /// ```
77
    #[inline]
78
    fn eq(&self, other: &ObjectPathInner) -> bool {
1✔
79
        self == &other.0
1✔
80
    }
1✔
81
}
82

83
impl PartialEq<String> for ObjectPathInner<'_> {
84
    /// 相等比较
85
    /// ```
86
    /// # use aliyun_oss_client::types::object::ObjectPath;
87
    /// let path = ObjectPath::new("abc").unwrap();
88
    /// assert!(path == "abc".to_string());
89
    /// ```
90
    #[inline]
91
    fn eq(&self, other: &String) -> bool {
1✔
92
        &self.0 == other
1✔
93
    }
1✔
94
}
95

96
impl PartialEq<ObjectPathInner<'_>> for String {
97
    /// 相等比较
98
    /// ```
99
    /// # use aliyun_oss_client::types::object::ObjectPath;
100
    /// let path = ObjectPath::new("abc").unwrap();
101
    /// assert!("abc".to_string() == path);
102
    /// ```
103
    #[inline]
104
    fn eq(&self, other: &ObjectPathInner) -> bool {
1✔
105
        self == &other.0
1✔
106
    }
1✔
107
}
108

109
impl<'a> ObjectPathInner<'a> {
110
    /// Creates a new `ObjectPath` from the given string.
111
    /// ```
112
    /// # use aliyun_oss_client::types::object::ObjectPath;
113
    /// assert!(ObjectPath::new("abc.jpg").is_ok());
114
    /// assert!(ObjectPath::new("abc/def.jpg").is_ok());
115
    /// assert!(ObjectPath::new("/").is_err());
116
    /// assert!(ObjectPath::new("/abc").is_err());
117
    /// assert!(ObjectPath::new("abc/").is_err());
118
    /// assert!(ObjectPath::new(".abc").is_err());
119
    /// assert!(ObjectPath::new("../abc").is_err());
120
    /// assert!(ObjectPath::new(r"aaa\abc").is_err());
121
    /// ```
122
    pub fn new(val: impl Into<Cow<'a, str>>) -> Result<Self, InvalidObjectPath> {
76✔
123
        let val = val.into();
76✔
124
        if val.starts_with('/') || val.starts_with('.') || val.ends_with('/') {
76✔
125
            return Err(InvalidObjectPath { _priv: () });
12✔
126
        }
127
        if !val.chars().all(|c| c != '\\') {
638✔
128
            return Err(InvalidObjectPath { _priv: () });
1✔
129
        }
130
        Ok(Self(val))
63✔
131
    }
76✔
132

133
    /// # Safety
134
    ///
135
    /// Const function that creates a new `ObjectPath` from a static str.
136
    /// ```
137
    /// # use aliyun_oss_client::types::object::ObjectPath;
138
    /// let path = unsafe { ObjectPath::from_static("abc") };
139
    /// assert!(path == "abc");
140
    /// ```
141
    pub const unsafe fn from_static(secret: &'static str) -> Self {
1✔
142
        Self(Cow::Borrowed(secret))
1✔
143
    }
1✔
144
}
145

146
impl TryFrom<String> for ObjectPathInner<'_> {
147
    type Error = InvalidObjectPath;
148
    /// ```
149
    /// # use aliyun_oss_client::types::object::ObjectPath;
150
    /// let path: ObjectPath = String::from("abc").try_into().unwrap();
151
    /// assert!(path == "abc");
152
    /// ```
153
    fn try_from(val: String) -> Result<Self, Self::Error> {
10✔
154
        Self::new(val)
10✔
155
    }
10✔
156
}
157

158
impl TryFrom<&String> for ObjectPathInner<'_> {
159
    type Error = InvalidObjectPath;
160
    /// ```
161
    /// # use aliyun_oss_client::types::object::ObjectPath;
162
    /// let path: ObjectPath = String::from("abc").try_into().unwrap();
163
    /// assert!(path == "abc");
164
    /// ```
165
    fn try_from(val: &String) -> Result<Self, Self::Error> {
×
166
        Self::new(val.to_owned())
×
167
    }
×
168
}
169

170
impl TryFrom<Box<String>> for ObjectPathInner<'_> {
171
    type Error = InvalidObjectPath;
172
    fn try_from(val: Box<String>) -> Result<Self, Self::Error> {
×
173
        Self::new(*val)
×
174
    }
×
175
}
176

177
impl<'a: 'b, 'b> TryFrom<&'a str> for ObjectPathInner<'b> {
178
    type Error = InvalidObjectPath;
179
    fn try_from(val: &'a str) -> Result<Self, Self::Error> {
46✔
180
        Self::new(val)
46✔
181
    }
46✔
182
}
183

184
impl FromStr for ObjectPathInner<'_> {
185
    type Err = InvalidObjectPath;
186
    /// ```
187
    /// # use aliyun_oss_client::types::object::ObjectPath;
188
    /// use std::str::FromStr;
189
    /// let path: ObjectPath = "img1.jpg".parse().unwrap();
190
    /// assert!(path == "img1.jpg");
191
    /// assert!(ObjectPath::from_str("abc.jpg").is_ok());
192
    /// assert!(ObjectPath::from_str("abc/def.jpg").is_ok());
193
    /// assert!(ObjectPath::from_str("/").is_err());
194
    /// assert!(ObjectPath::from_str("/abc").is_err());
195
    /// assert!(ObjectPath::from_str("abc/").is_err());
196
    /// assert!(ObjectPath::from_str(".abc").is_err());
197
    /// assert!(ObjectPath::from_str("../abc").is_err());
198
    /// assert!(ObjectPath::from_str(r"aaa\abc").is_err());
199
    /// ```
200
    fn from_str(s: &str) -> Result<Self, Self::Err> {
48✔
201
        if s.starts_with('/') || s.starts_with('.') || s.ends_with('/') {
48✔
202
            return Err(InvalidObjectPath { _priv: () });
6✔
203
        }
204

205
        if !s.chars().all(|c| c != '\\') {
309✔
206
            return Err(InvalidObjectPath { _priv: () });
1✔
207
        }
208
        Ok(Self(Cow::Owned(s.to_owned())))
41✔
209
    }
48✔
210
}
211

212
impl TryFrom<&[u8]> for ObjectPathInner<'_> {
213
    type Error = InvalidObjectPath;
214
    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
×
215
        use std::str;
216

217
        str::from_utf8(value)
×
218
            .map_err(|_| InvalidObjectPath { _priv: () })
×
219
            .and_then(|s| s.parse())
×
220
    }
×
221
}
222

223
impl TryFrom<&Path> for ObjectPathInner<'_> {
224
    type Error = InvalidObjectPath;
225
    fn try_from(value: &Path) -> Result<Self, Self::Error> {
1✔
226
        let val = value.to_str().ok_or(InvalidObjectPath { _priv: () })?;
1✔
227
        if std::path::MAIN_SEPARATOR != '/' {
228
            val.replace(std::path::MAIN_SEPARATOR, "/").parse()
229
        } else {
230
            val.parse()
1✔
231
        }
232
    }
1✔
233
}
234

235
/// 不合法的文件路径
236
pub struct InvalidObjectPath {
237
    pub(crate) _priv: (),
238
}
239

240
impl Display for InvalidObjectPath {
241
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4✔
242
        write!(f, "invalid object path")
4✔
243
    }
4✔
244
}
245

246
impl Debug for InvalidObjectPath {
247
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4✔
248
        f.debug_struct("InvalidObjectPath").finish()
4✔
249
    }
4✔
250
}
251

252
impl std::error::Error for InvalidObjectPath {}
253

254
impl From<Infallible> for InvalidObjectPath {
255
    fn from(_: Infallible) -> Self {
×
256
        Self { _priv: () }
257
    }
×
258
}
259

260
/// 将 object 的路径拼接到 Url 上去
261
pub trait SetObjectPath {
262
    /// 为 Url 添加方法
263
    fn set_object_path(&mut self, path: &ObjectPathInner);
264
}
265

266
impl SetObjectPath for Url {
267
    fn set_object_path(&mut self, path: &ObjectPathInner) {
50✔
268
        self.set_path(path.as_ref());
50✔
269
    }
50✔
270
}
271

272
/// OSS Object 对象路径的前缀目录
273
/// 不带前缀 `/`, 且必须以 `/` 结尾
274
#[derive(Debug, Clone, PartialEq, Eq)]
10✔
275
pub struct ObjectDir<'a>(Cow<'a, str>);
5✔
276

277
impl AsRef<str> for ObjectDir<'_> {
278
    fn as_ref(&self) -> &str {
1✔
279
        &self.0
1✔
280
    }
1✔
281
}
282

283
impl AsMut<String> for ObjectDir<'_> {
284
    fn as_mut(&mut self) -> &mut String {
1✔
285
        self.0.to_mut()
1✔
286
    }
1✔
287
}
288

289
impl fmt::Display for ObjectDir<'_> {
290
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1✔
291
        write!(f, "{}", self.0)
1✔
292
    }
1✔
293
}
294

295
// impl Default for ObjectDir<'_> {
296
//     /// 默认值
297
//     /// ```
298
//     /// # use aliyun_oss_client::types::object::ObjectDir;
299
//     /// let path = ObjectDir::default();
300
//     /// assert!(path == "default/");
301
//     /// ```
302
//     fn default() -> Self {
303
//         Self(Cow::Borrowed("default/"))
304
//     }
305
// }
306

307
impl PartialEq<&str> for ObjectDir<'_> {
308
    /// 相等比较
309
    /// ```
310
    /// # use aliyun_oss_client::types::object::ObjectDir;
311
    /// let path = ObjectDir::new("abc/").unwrap();
312
    /// assert!(path == "abc/");
313
    /// ```
314
    #[inline]
315
    fn eq(&self, other: &&str) -> bool {
2✔
316
        &self.0 == other
2✔
317
    }
2✔
318
}
319

320
impl PartialEq<ObjectDir<'_>> for &str {
321
    /// 相等比较
322
    /// ```
323
    /// # use aliyun_oss_client::types::object::ObjectDir;
324
    /// let path = ObjectDir::new("abc/").unwrap();
325
    /// assert!("abc/" == path);
326
    /// ```
327
    #[inline]
328
    fn eq(&self, other: &ObjectDir<'_>) -> bool {
1✔
329
        self == &other.0
1✔
330
    }
1✔
331
}
332

333
impl PartialEq<String> for ObjectDir<'_> {
334
    /// 相等比较
335
    /// ```
336
    /// # use aliyun_oss_client::types::object::ObjectDir;
337
    /// let path = ObjectDir::new("abc/").unwrap();
338
    /// assert!(path == "abc/".to_string());
339
    /// ```
340
    #[inline]
341
    fn eq(&self, other: &String) -> bool {
1✔
342
        &self.0 == other
1✔
343
    }
1✔
344
}
345

346
impl PartialEq<ObjectDir<'_>> for String {
347
    /// 相等比较
348
    /// ```
349
    /// # use aliyun_oss_client::types::object::ObjectDir;
350
    /// let path = ObjectDir::new("abc/").unwrap();
351
    /// assert!("abc/".to_string() == path);
352
    /// ```
353
    #[inline]
354
    fn eq(&self, other: &ObjectDir) -> bool {
1✔
355
        self == &other.0
1✔
356
    }
1✔
357
}
358

359
impl<'dir1, 'dir2: 'dir1> Add<ObjectDir<'dir2>> for ObjectDir<'dir1> {
360
    type Output = ObjectDir<'dir1>;
361

362
    /// # 支持 ObjectDir 相加运算
363
    /// ```
364
    /// # use aliyun_oss_client::types::object::ObjectDir;
365
    /// let dir1 = ObjectDir::new("dir1/").unwrap();
366
    /// let dir2 = ObjectDir::new("dir2/").unwrap();
367
    /// let full_dir = ObjectDir::new("dir1/dir2/").unwrap();
368
    ///
369
    /// assert_eq!(dir1 + dir2, full_dir);
370
    /// ```
371
    fn add(self, rhs: ObjectDir<'dir2>) -> Self::Output {
1✔
372
        let mut string = self.0;
1✔
373

374
        string += rhs.0;
1✔
375
        ObjectDir(string)
1✔
376
    }
1✔
377
}
378

379
impl<'dir1, 'dir2: 'dir1> AddAssign<ObjectDir<'dir2>> for ObjectDir<'dir1> {
380
    /// # 支持 ObjectDir 相加运算
381
    /// ```
382
    /// # use aliyun_oss_client::types::object::ObjectDir;
383
    /// let mut dir1 = ObjectDir::new("dir1/").unwrap();
384
    /// let dir2 = ObjectDir::new("dir2/").unwrap();
385
    /// let full_dir = ObjectDir::new("dir1/dir2/").unwrap();
386
    ///
387
    /// dir1 += dir2;
388
    /// assert_eq!(dir1, full_dir);
389
    /// ```
390
    fn add_assign(&mut self, rhs: ObjectDir<'dir2>) {
1✔
391
        *self.as_mut() += rhs.as_ref();
1✔
392
    }
1✔
393
}
394

395
impl<'file, 'dir: 'file> Add<ObjectPathInner<'file>> for ObjectDir<'dir> {
396
    type Output = ObjectPathInner<'file>;
397

398
    /// # 支持 ObjectDir 与 ObjectPath 相加运算
399
    /// ```
400
    /// # use aliyun_oss_client::types::object::{ObjectDir, ObjectPath};
401
    /// let dir1 = ObjectDir::new("dir1/").unwrap();
402
    /// let file1 = ObjectPath::new("img1.png").unwrap();
403
    /// let full_file = ObjectPath::new("dir1/img1.png").unwrap();
404
    ///
405
    /// assert_eq!(dir1 + file1, full_file);
406
    /// ```
407
    fn add(self, rhs: ObjectPathInner<'file>) -> Self::Output {
1✔
408
        let mut string = self.0;
1✔
409

410
        string += rhs.0;
1✔
411
        ObjectPathInner(string)
1✔
412
    }
1✔
413
}
414

415
impl<'a> ObjectDir<'a> {
416
    /// Creates a new `ObjectPath` from the given string.
417
    /// ```
418
    /// # use aliyun_oss_client::types::object::ObjectDir;
419
    /// assert!(ObjectDir::new("abc/").is_ok());
420
    /// assert!(ObjectDir::new("abc/def/").is_ok());
421
    /// assert!(ObjectDir::new("/").is_err());
422
    /// assert!(ObjectDir::new("/abc/").is_err());
423
    /// assert!(ObjectDir::new(".abc/").is_err());
424
    /// assert!(ObjectDir::new("../abc/").is_err());
425
    /// assert!(ObjectDir::new(r"aaa\abc/").is_err());
426
    /// ```
427
    pub fn new<'b: 'a>(val: impl Into<Cow<'b, str>>) -> Result<Self, InvalidObjectDir> {
27✔
428
        let val = val.into();
27✔
429
        if val.starts_with('/') || val.starts_with('.') || !val.ends_with('/') {
27✔
430
            return Err(InvalidObjectDir { _priv: () });
5✔
431
        }
432
        if !val.chars().all(|c| c != '\\') {
138✔
433
            return Err(InvalidObjectDir { _priv: () });
1✔
434
        }
435
        Ok(Self(val))
21✔
436
    }
27✔
437

438
    /// # Safety
439
    ///
440
    /// Const function that creates a new `ObjectPath` from a static str.
441
    /// ```
442
    /// # use aliyun_oss_client::types::object::ObjectDir;
443
    /// let path = unsafe { ObjectDir::from_static("abc/") };
444
    /// assert!(path == "abc/");
445
    /// ```
446
    pub const unsafe fn from_static(secret: &'a str) -> Self {
1✔
447
        Self(Cow::Borrowed(secret))
1✔
448
    }
1✔
449
}
450

451
impl TryFrom<String> for ObjectDir<'_> {
452
    type Error = InvalidObjectDir;
453
    /// ```
454
    /// # use aliyun_oss_client::types::object::ObjectDir;
455
    /// let path: ObjectDir = String::from("abc/").try_into().unwrap();
456
    /// assert!(path == "abc/");
457
    /// ```
458
    fn try_from(val: String) -> Result<Self, Self::Error> {
1✔
459
        Self::new(val)
1✔
460
    }
1✔
461
}
462

463
impl<'a: 'b, 'b> TryFrom<&'a str> for ObjectDir<'b> {
464
    type Error = InvalidObjectDir;
465
    fn try_from(val: &'a str) -> Result<Self, Self::Error> {
2✔
466
        Self::new(val)
2✔
467
    }
2✔
468
}
469

470
impl FromStr for ObjectDir<'_> {
471
    type Err = InvalidObjectDir;
472
    /// ```
473
    /// # use aliyun_oss_client::types::object::ObjectDir;
474
    /// use std::str::FromStr;
475
    /// let path: ObjectDir = "path1/".parse().unwrap();
476
    /// assert!(path == "path1/");
477
    /// assert!(ObjectDir::from_str("abc/").is_ok());
478
    /// assert!(ObjectDir::from_str("abc/def/").is_ok());
479
    /// assert!(ObjectDir::from_str("/").is_err());
480
    /// assert!(ObjectDir::from_str("/abc/").is_err());
481
    /// assert!(ObjectDir::from_str(".abc/").is_err());
482
    /// assert!(ObjectDir::from_str("../abc/").is_err());
483
    /// assert!(ObjectDir::from_str(r"aaa\abc/").is_err());
484
    /// ```
485
    fn from_str(s: &str) -> Result<Self, Self::Err> {
24✔
486
        if s.starts_with('/') || s.starts_with('.') || !s.ends_with('/') {
24✔
487
            return Err(InvalidObjectDir { _priv: () });
6✔
488
        }
489

490
        if !s.chars().all(|c| c != '\\') {
108✔
491
            return Err(InvalidObjectDir { _priv: () });
1✔
492
        }
493
        Ok(Self(Cow::Owned(s.to_owned())))
17✔
494
    }
24✔
495
}
496

497
impl TryFrom<&Path> for ObjectDir<'_> {
498
    type Error = InvalidObjectDir;
499
    fn try_from(value: &Path) -> Result<Self, Self::Error> {
1✔
500
        let val = value.to_str().ok_or(InvalidObjectDir { _priv: () })?;
1✔
501
        if std::path::MAIN_SEPARATOR != '/' {
502
            val.replace(std::path::MAIN_SEPARATOR, "/").parse()
503
        } else {
504
            val.parse()
1✔
505
        }
506
    }
1✔
507
}
508

509
/// 不合法的文件目录路径
510
pub struct InvalidObjectDir {
511
    pub(crate) _priv: (),
512
}
513

514
impl Display for InvalidObjectDir {
515
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4✔
516
        write!(
4✔
517
            f,
518
            "object-dir must end with `/`, and not start with `/`,`.`"
519
        )
520
    }
4✔
521
}
522

523
impl Debug for InvalidObjectDir {
524
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4✔
525
        f.debug_struct("InvalidObjectDir").finish()
4✔
526
    }
4✔
527
}
528

529
impl std::error::Error for InvalidObjectDir {}
530

531
/// 根据 OSS 的配置信息初始化外部类型
532
pub trait FromOss {
533
    /// 根据配置信息,计算外部类型的具体实现
534
    fn from_oss(endpoint: &EndPoint, bucket: &BucketName, path: &ObjectPath) -> Self;
535
}
536

537
/// 给 Url 设置一个初始化方法,根据 OSS 的配置信息,返回文件的完整 OSS Url
538
impl FromOss for Url {
539
    /// 根据配置信息,计算完整的 Url
540
    fn from_oss(endpoint: &EndPoint, bucket: &BucketName, path: &ObjectPath) -> Self {
1✔
541
        let mut end_url = endpoint.to_url();
1✔
542

543
        let host = end_url.host_str();
1✔
544

545
        let mut name_str = bucket.to_string() + ".";
1✔
546

547
        let new_host = host.map(|h| {
2✔
548
            name_str.push_str(h);
1✔
549
            &*name_str
1✔
550
        });
1✔
551
        // 因为 endpoint 都是已知字符组成,bucket 也有格式要求,所以 unwrap 是安全的
552
        end_url
2✔
553
            .set_host(new_host)
1✔
554
            .unwrap_or_else(|_| panic!("set host failed: host: {}", new_host.unwrap_or("none")));
1✔
555

556
        end_url.set_object_path(path);
1✔
557

558
        end_url
559
    }
1✔
560
}
561

562
/// 文件夹下的子文件夹名,子文件夹下递归的所有文件和文件夹不包含在这里。
563
pub type CommonPrefixes = Vec<ObjectDir<'static>>;
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