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

tu6ge / oss-rs / 5829505807

pending completion
5829505807

push

github

tu6ge
Support std IO (#26)

* feat(decode)!: change init object fn

* todo

* feat(error): OssError add more info

when OssError code is SignatureDoesNotMatch ,show expect
 sign string

* feat(io): support write

* feat: blocking support

* feat: blocking read

* feat: 允许读取的数据于目标数组长度不一致

* feat: 分离 Rc 和内部数据

* feat: support Arc Object Content

* feat: 解决多次写入少量数据导致oss错误的问题

当多次写入少量数据,不符合分片的最小数量时,调用 oss 接口会导致报错

* refactor

* feat: 交互 arc 与 rc 的位置

* docs(io)

* docs(io)

* style

* chore: default close blocking

* fix

* style

* feat(io): change seek

* feat(io): change error type

* style

* feat(bucket)!: change base_bucket_info

* test(io)

* test(doc): remove deprecated

* test(io)

* test(io)

* test(io)

* style(io): clippy

* chore: support more derive

* refactor

* docs

1293 of 1293 new or added lines in 19 files covered. (100.0%)

7298 of 7685 relevant lines covered (94.96%)

9.62 hits per line

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

98.13
/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, PartialOrd, Ord, Hash)]
62✔
27
pub struct ObjectPathInner<'a>(Cow<'a, str>);
31✔
28

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

33
impl AsRef<str> for ObjectPathInner<'_> {
34
    fn as_ref(&self) -> &str {
171✔
35
        &self.0
171✔
36
    }
171✔
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 {
76✔
53
        Self(Cow::Borrowed(""))
76✔
54
    }
76✔
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 {
9✔
66
        &self.0 == other
9✔
67
    }
9✔
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> {
96✔
123
        let val = val.into();
96✔
124
        if val.starts_with('/') || val.starts_with('.') || val.ends_with('/') {
96✔
125
            return Err(InvalidObjectPath::new());
12✔
126
        }
127
        if !val.chars().all(|c| c != '\\') {
794✔
128
            return Err(InvalidObjectPath::new());
1✔
129
        }
130
        Ok(Self(val))
83✔
131
    }
96✔
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> {
12✔
154
        Self::new(val)
12✔
155
    }
12✔
156
}
157

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

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

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

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

207
        if !s.chars().all(|c| c != '\\') {
322✔
208
            return Err(InvalidObjectPath::new());
1✔
209
        }
210
        Ok(Self(Cow::Owned(s.to_owned())))
43✔
211
    }
50✔
212
}
213

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

219
        str::from_utf8(value)
1✔
220
            .map_err(|_| InvalidObjectPath::new())
×
221
            .and_then(str::parse)
222
    }
1✔
223
}
224

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

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

242
impl InvalidObjectPath {
243
    pub(crate) fn new() -> Self {
25✔
244
        Self { _priv: () }
245
    }
25✔
246
}
247

248
impl Display for InvalidObjectPath {
249
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4✔
250
        write!(f, "invalid object path")
4✔
251
    }
4✔
252
}
253

254
impl Debug for InvalidObjectPath {
255
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4✔
256
        f.debug_struct("InvalidObjectPath").finish()
4✔
257
    }
4✔
258
}
259

260
impl std::error::Error for InvalidObjectPath {}
261

262
impl From<Infallible> for InvalidObjectPath {
263
    fn from(_: Infallible) -> Self {
×
264
        Self { _priv: () }
265
    }
×
266
}
267

268
/// 将 object 的路径拼接到 Url 上去
269
pub trait SetObjectPath: private::Sealed {
270
    /// 为 Url 添加方法
271
    fn set_object_path(&mut self, path: &ObjectPathInner);
272
}
273

274
mod private {
275
    pub trait Sealed {}
276
}
277

278
impl private::Sealed for Url {}
279

280
impl SetObjectPath for Url {
281
    fn set_object_path(&mut self, path: &ObjectPathInner) {
68✔
282
        self.set_path(path.as_ref());
68✔
283
    }
68✔
284
}
285

286
/// OSS Object 对象路径的前缀目录
287
/// 不带前缀 `/`, 且必须以 `/` 结尾
288
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
10✔
289
pub struct ObjectDir<'a>(Cow<'a, str>);
5✔
290

291
impl AsRef<str> for ObjectDir<'_> {
292
    fn as_ref(&self) -> &str {
1✔
293
        &self.0
1✔
294
    }
1✔
295
}
296

297
impl AsMut<String> for ObjectDir<'_> {
298
    fn as_mut(&mut self) -> &mut String {
1✔
299
        self.0.to_mut()
1✔
300
    }
1✔
301
}
302

303
impl fmt::Display for ObjectDir<'_> {
304
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1✔
305
        write!(f, "{}", self.0)
1✔
306
    }
1✔
307
}
308

309
// impl Default for ObjectDir<'_> {
310
//     /// 默认值
311
//     /// ```
312
//     /// # use aliyun_oss_client::types::object::ObjectDir;
313
//     /// let path = ObjectDir::default();
314
//     /// assert!(path == "default/");
315
//     /// ```
316
//     fn default() -> Self {
317
//         Self(Cow::Borrowed("default/"))
318
//     }
319
// }
320

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

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

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

360
impl PartialEq<ObjectDir<'_>> for String {
361
    /// 相等比较
362
    /// ```
363
    /// # use aliyun_oss_client::types::object::ObjectDir;
364
    /// let path = ObjectDir::new("abc/").unwrap();
365
    /// assert!("abc/".to_string() == path);
366
    /// ```
367
    #[inline]
368
    fn eq(&self, other: &ObjectDir) -> bool {
1✔
369
        self == &other.0
1✔
370
    }
1✔
371
}
372

373
impl<'dir1, 'dir2: 'dir1> Add<ObjectDir<'dir2>> for ObjectDir<'dir1> {
374
    type Output = ObjectDir<'dir1>;
375

376
    /// # 支持 ObjectDir 相加运算
377
    /// ```
378
    /// # use aliyun_oss_client::types::object::ObjectDir;
379
    /// let dir1 = ObjectDir::new("dir1/").unwrap();
380
    /// let dir2 = ObjectDir::new("dir2/").unwrap();
381
    /// let full_dir = ObjectDir::new("dir1/dir2/").unwrap();
382
    ///
383
    /// assert_eq!(dir1 + dir2, full_dir);
384
    /// ```
385
    fn add(self, rhs: ObjectDir<'dir2>) -> Self::Output {
1✔
386
        let mut string = self.0;
1✔
387

388
        string += rhs.0;
1✔
389
        ObjectDir(string)
1✔
390
    }
1✔
391
}
392

393
impl<'dir1, 'dir2: 'dir1> AddAssign<ObjectDir<'dir2>> for ObjectDir<'dir1> {
394
    /// # 支持 ObjectDir 相加运算
395
    /// ```
396
    /// # use aliyun_oss_client::types::object::ObjectDir;
397
    /// let mut dir1 = ObjectDir::new("dir1/").unwrap();
398
    /// let dir2 = ObjectDir::new("dir2/").unwrap();
399
    /// let full_dir = ObjectDir::new("dir1/dir2/").unwrap();
400
    ///
401
    /// dir1 += dir2;
402
    /// assert_eq!(dir1, full_dir);
403
    /// ```
404
    fn add_assign(&mut self, rhs: ObjectDir<'dir2>) {
1✔
405
        *self.as_mut() += rhs.as_ref();
1✔
406
    }
1✔
407
}
408

409
impl<'file, 'dir: 'file> Add<ObjectPathInner<'file>> for ObjectDir<'dir> {
410
    type Output = ObjectPathInner<'file>;
411

412
    /// # 支持 ObjectDir 与 ObjectPath 相加运算
413
    /// ```
414
    /// # use aliyun_oss_client::types::object::{ObjectDir, ObjectPath};
415
    /// let dir1 = ObjectDir::new("dir1/").unwrap();
416
    /// let file1 = ObjectPath::new("img1.png").unwrap();
417
    /// let full_file = ObjectPath::new("dir1/img1.png").unwrap();
418
    ///
419
    /// assert_eq!(dir1 + file1, full_file);
420
    /// ```
421
    fn add(self, rhs: ObjectPathInner<'file>) -> Self::Output {
1✔
422
        let mut string = self.0;
1✔
423

424
        string += rhs.0;
1✔
425
        ObjectPathInner(string)
1✔
426
    }
1✔
427
}
428

429
impl<'a> ObjectDir<'a> {
430
    /// Creates a new `ObjectPath` from the given string.
431
    /// ```
432
    /// # use aliyun_oss_client::types::object::ObjectDir;
433
    /// assert!(ObjectDir::new("abc/").is_ok());
434
    /// assert!(ObjectDir::new("abc/def/").is_ok());
435
    /// assert!(ObjectDir::new("/").is_err());
436
    /// assert!(ObjectDir::new("/abc/").is_err());
437
    /// assert!(ObjectDir::new(".abc/").is_err());
438
    /// assert!(ObjectDir::new("../abc/").is_err());
439
    /// assert!(ObjectDir::new(r"aaa\abc/").is_err());
440
    /// ```
441
    pub fn new<'b: 'a>(val: impl Into<Cow<'b, str>>) -> Result<Self, InvalidObjectDir> {
27✔
442
        let val = val.into();
27✔
443
        if val.starts_with('/') || val.starts_with('.') || !val.ends_with('/') {
27✔
444
            return Err(InvalidObjectDir::new());
5✔
445
        }
446
        if !val.chars().all(|c| c != '\\') {
138✔
447
            return Err(InvalidObjectDir::new());
1✔
448
        }
449
        Ok(Self(val))
21✔
450
    }
27✔
451

452
    /// # Safety
453
    ///
454
    /// Const function that creates a new `ObjectPath` from a static str.
455
    /// ```
456
    /// # use aliyun_oss_client::types::object::ObjectDir;
457
    /// let path = unsafe { ObjectDir::from_static("abc/") };
458
    /// assert!(path == "abc/");
459
    /// ```
460
    pub const unsafe fn from_static(secret: &'a str) -> Self {
1✔
461
        Self(Cow::Borrowed(secret))
1✔
462
    }
1✔
463
}
464

465
impl TryFrom<String> for ObjectDir<'_> {
466
    type Error = InvalidObjectDir;
467
    /// ```
468
    /// # use aliyun_oss_client::types::object::ObjectDir;
469
    /// let path: ObjectDir = String::from("abc/").try_into().unwrap();
470
    /// assert!(path == "abc/");
471
    /// ```
472
    fn try_from(val: String) -> Result<Self, Self::Error> {
1✔
473
        Self::new(val)
1✔
474
    }
1✔
475
}
476

477
impl<'a: 'b, 'b> TryFrom<&'a str> for ObjectDir<'b> {
478
    type Error = InvalidObjectDir;
479
    fn try_from(val: &'a str) -> Result<Self, Self::Error> {
2✔
480
        Self::new(val)
2✔
481
    }
2✔
482
}
483

484
impl FromStr for ObjectDir<'_> {
485
    type Err = InvalidObjectDir;
486
    /// ```
487
    /// # use aliyun_oss_client::types::object::ObjectDir;
488
    /// use std::str::FromStr;
489
    /// let path: ObjectDir = "path1/".parse().unwrap();
490
    /// assert!(path == "path1/");
491
    /// assert!(ObjectDir::from_str("abc/").is_ok());
492
    /// assert!(ObjectDir::from_str("abc/def/").is_ok());
493
    /// assert!(ObjectDir::from_str("/").is_err());
494
    /// assert!(ObjectDir::from_str("/abc/").is_err());
495
    /// assert!(ObjectDir::from_str(".abc/").is_err());
496
    /// assert!(ObjectDir::from_str("../abc/").is_err());
497
    /// assert!(ObjectDir::from_str(r"aaa\abc/").is_err());
498
    /// ```
499
    fn from_str(s: &str) -> Result<Self, Self::Err> {
24✔
500
        if s.starts_with('/') || s.starts_with('.') || !s.ends_with('/') {
24✔
501
            return Err(InvalidObjectDir::new());
6✔
502
        }
503

504
        if !s.chars().all(|c| c != '\\') {
108✔
505
            return Err(InvalidObjectDir::new());
1✔
506
        }
507
        Ok(Self(Cow::Owned(s.to_owned())))
17✔
508
    }
24✔
509
}
510

511
impl TryFrom<&Path> for ObjectDir<'_> {
512
    type Error = InvalidObjectDir;
513
    fn try_from(value: &Path) -> Result<Self, Self::Error> {
1✔
514
        let val = value.to_str().ok_or(InvalidObjectDir::new())?;
1✔
515
        if std::path::MAIN_SEPARATOR != '/' {
516
            val.replace(std::path::MAIN_SEPARATOR, "/").parse()
517
        } else {
518
            val.parse()
1✔
519
        }
520
    }
1✔
521
}
522

523
/// 不合法的文件目录路径
524
pub struct InvalidObjectDir {
525
    _priv: (),
526
}
527

528
impl InvalidObjectDir {
529
    pub(crate) fn new() -> InvalidObjectDir {
17✔
530
        InvalidObjectDir { _priv: () }
531
    }
17✔
532
}
533

534
impl Display for InvalidObjectDir {
535
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4✔
536
        write!(
4✔
537
            f,
538
            "object-dir must end with `/`, and not start with `/`,`.`"
539
        )
540
    }
4✔
541
}
542

543
impl Debug for InvalidObjectDir {
544
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4✔
545
        f.debug_struct("InvalidObjectDir").finish()
4✔
546
    }
4✔
547
}
548

549
impl std::error::Error for InvalidObjectDir {}
550

551
/// 根据 OSS 的配置信息初始化外部类型
552
pub trait FromOss {
553
    /// 根据配置信息,计算外部类型的具体实现
554
    fn from_oss(endpoint: &EndPoint, bucket: &BucketName, path: &ObjectPath) -> Self;
555
}
556

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

563
        let host = end_url.host_str();
1✔
564

565
        let mut name_str = bucket.to_string() + ".";
1✔
566

567
        let new_host = host.map(|h| {
2✔
568
            name_str.push_str(h);
1✔
569
            &*name_str
1✔
570
        });
1✔
571
        // 因为 endpoint 都是已知字符组成,bucket 也有格式要求,所以 unwrap 是安全的
572
        end_url
2✔
573
            .set_host(new_host)
1✔
574
            .unwrap_or_else(|_| panic!("set host failed: host: {}", new_host.unwrap_or("none")));
1✔
575

576
        end_url.set_object_path(path);
1✔
577

578
        end_url
579
    }
1✔
580
}
581

582
/// 文件夹下的子文件夹名,子文件夹下递归的所有文件和文件夹不包含在这里。
583
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