qiniu_objects_manager/
operation.rs

1use super::{mime::Mime, Bucket};
2use anyhow::{Error as AnyError, Result as AnyResult};
3use assert_impl::assert_impl;
4use auto_impl::auto_impl;
5use dyn_clonable::clonable;
6use indexmap::IndexMap;
7use qiniu_apis::{
8    http::{ResponseError as HttpResponseError, ResponseErrorKind as HttpResponseErrorKind},
9    http_client::{ApiResult, RegionsProviderEndpoints, RequestBuilderParts, Response},
10    upload_token::FileType,
11};
12use qiniu_utils::base64::urlsafe;
13use std::{
14    fmt::{self, Debug, Display},
15    mem::take,
16    sync::{Arc, Mutex},
17};
18
19macro_rules! impl_call_methods {
20    ($mod_name:ident) => {
21        impl_call_methods!($mod_name, entry);
22    };
23    ($mod_name:ident, $entry:ident) => {
24        /// 阻塞发起操作请求
25        ///
26        /// 该方法的异步版本为 [`Self::async_call`]。
27        pub fn call(&mut self) -> ApiResult<Response<qiniu_apis::storage::$mod_name::ResponseBody>> {
28            let op = self.build();
29            let mut request = op
30                .$entry
31                .bucket
32                .objects_manager()
33                .client()
34                .storage()
35                .$mod_name()
36                .new_request(
37                    RegionsProviderEndpoints::new(op.$entry.bucket.region_provider()?),
38                    op.to_path_params(),
39                    op.$entry.bucket.objects_manager().credential(),
40                );
41            if let Some(callback) = &self.before_request_callback {
42                let mut callback = callback.lock().unwrap();
43                callback(request.parts_mut()).map_err(make_callback_error)?;
44            }
45            request.call()
46        }
47
48        #[cfg(feature = "async")]
49        #[cfg_attr(feature = "docs", doc(cfg(feature = "async")))]
50        /// 异步发起操作请求
51        pub async fn async_call(&mut self) -> ApiResult<Response<qiniu_apis::storage::$mod_name::ResponseBody>> {
52            let op = self.build();
53            let mut request = op
54                .$entry
55                .bucket
56                .objects_manager()
57                .client()
58                .storage()
59                .$mod_name()
60                .new_async_request(
61                    RegionsProviderEndpoints::new(op.$entry.bucket.region_provider()?),
62                    op.to_path_params(),
63                    op.$entry.bucket.objects_manager().credential(),
64                );
65            if let Some(callback) = &self.before_request_callback {
66                let mut callback = callback.lock().unwrap();
67                callback(request.parts_mut()).map_err(make_callback_error)?;
68            }
69            request.call().await
70        }
71
72        #[allow(dead_code)]
73        fn assert() {
74            assert_impl!(Send: Self);
75            assert_impl!(Sync: Self);
76        }
77    };
78}
79
80type BeforeRequestCallback<'c> =
81    Arc<Mutex<dyn FnMut(&mut RequestBuilderParts<'_>) -> AnyResult<()> + Send + Sync + 'c>>;
82
83#[derive(Clone, Debug)]
84pub(super) struct Entry<'a> {
85    bucket: &'a Bucket,
86    object: &'a str,
87}
88
89impl<'a> Entry<'a> {
90    pub(super) fn new(bucket: &'a Bucket, object: &'a str) -> Self {
91        Self { bucket, object }
92    }
93
94    fn encode(&self) -> String {
95        urlsafe(self.to_string().as_bytes())
96    }
97}
98
99impl Display for Entry<'_> {
100    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101        write!(f, "{}:{}", self.bucket.name(), self.object)
102    }
103}
104
105#[derive(Clone, Debug)]
106struct SimpleEntry<'a> {
107    bucket: &'a str,
108    object: &'a str,
109}
110
111impl<'a> SimpleEntry<'a> {
112    pub(super) fn new(bucket: &'a str, object: &'a str) -> Self {
113        Self { bucket, object }
114    }
115
116    fn encode(&self) -> String {
117        urlsafe(self.to_string().as_bytes())
118    }
119}
120
121impl Display for SimpleEntry<'_> {
122    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
123        write!(f, "{}:{}", self.bucket, self.object)
124    }
125}
126
127/// 对象操作提供者接口
128#[clonable]
129#[auto_impl(&mut, Box)]
130pub trait OperationProvider: Clone + Debug + Sync + Send {
131    /// 转换为对象操作命令
132    fn to_operation(&mut self) -> String;
133}
134
135#[derive(Clone, Debug)]
136pub(super) struct StatObject<'a> {
137    entry: Entry<'a>,
138}
139
140impl StatObject<'_> {
141    pub(super) fn builder(entry: Entry) -> StatObjectBuilder {
142        StatObjectBuilder {
143            inner: StatObject { entry },
144            before_request_callback: None,
145        }
146    }
147
148    fn to_path_params(&self) -> qiniu_apis::storage::stat_object::PathParams {
149        qiniu_apis::storage::stat_object::PathParams::default().set_entry_as_str(self.entry.to_string())
150    }
151}
152
153impl Display for StatObject<'_> {
154    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
155        write!(f, "stat/{}", self.entry.encode())
156    }
157}
158
159/// 对象元信息获取操作构建器
160///
161/// 可以通过 [`crate::Bucket::stat_object`] 方法获取该构建器。
162#[derive(Clone)]
163pub struct StatObjectBuilder<'a> {
164    inner: StatObject<'a>,
165    before_request_callback: Option<BeforeRequestCallback<'a>>,
166}
167
168impl<'a> StatObjectBuilder<'a> {
169    /// 设置请求前回调函数
170    #[inline]
171    pub fn before_request_callback(
172        &mut self,
173        callback: impl FnMut(&mut RequestBuilderParts<'_>) -> AnyResult<()> + Send + Sync + 'a,
174    ) -> &mut Self {
175        self.before_request_callback = Some(Arc::new(Mutex::new(callback)));
176        self
177    }
178
179    fn build(&mut self) -> StatObject<'a> {
180        StatObject {
181            entry: self.inner.entry.to_owned(),
182        }
183    }
184
185    impl_call_methods!(stat_object);
186}
187
188impl OperationProvider for StatObjectBuilder<'_> {
189    fn to_operation(&mut self) -> String {
190        self.build().to_string()
191    }
192}
193
194impl Debug for StatObjectBuilder<'_> {
195    #[inline]
196    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
197        f.debug_struct("StatObjectBuilder").field("inner", &self.inner).finish()
198    }
199}
200
201#[derive(Clone, Debug)]
202pub(super) struct MoveObject<'a> {
203    from_entry: Entry<'a>,
204    to_entry: SimpleEntry<'a>,
205    is_force: bool,
206}
207
208impl MoveObject<'_> {
209    pub(super) fn builder<'a>(from_entry: Entry<'a>, to_bucket: &'a str, to_object: &'a str) -> MoveObjectBuilder<'a> {
210        MoveObjectBuilder {
211            inner: MoveObject {
212                from_entry,
213                to_entry: SimpleEntry::new(to_bucket, to_object),
214                is_force: false,
215            },
216            before_request_callback: None,
217        }
218    }
219
220    fn to_path_params(&self) -> qiniu_apis::storage::move_object::PathParams {
221        qiniu_apis::storage::move_object::PathParams::default()
222            .set_src_entry_as_str(self.from_entry.to_string())
223            .set_dest_entry_as_str(self.to_entry.to_string())
224            .set_is_force_as_bool(self.is_force)
225    }
226}
227
228impl Display for MoveObject<'_> {
229    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
230        write!(f, "move/{}/{}", self.from_entry.encode(), self.to_entry.encode())?;
231        if self.is_force {
232            write!(f, "/force/true")?;
233        }
234        Ok(())
235    }
236}
237
238/// 对象移动操作构建器
239///
240/// 可以通过 [`crate::Bucket::move_object_to`] 方法获取该构建器。
241#[derive(Clone)]
242pub struct MoveObjectBuilder<'a> {
243    inner: MoveObject<'a>,
244    before_request_callback: Option<BeforeRequestCallback<'a>>,
245}
246
247impl<'a> MoveObjectBuilder<'a> {
248    /// 是否强制移动
249    #[inline]
250    pub fn is_force(&mut self, is_force: bool) -> &mut Self {
251        self.inner.is_force = is_force;
252        self
253    }
254
255    /// 设置请求前回调函数
256    #[inline]
257    pub fn before_request_callback(
258        &mut self,
259        callback: impl FnMut(&mut RequestBuilderParts<'_>) -> AnyResult<()> + Send + Sync + 'a,
260    ) -> &mut Self {
261        self.before_request_callback = Some(Arc::new(Mutex::new(callback)));
262        self
263    }
264
265    fn build(&mut self) -> MoveObject<'a> {
266        MoveObject {
267            from_entry: self.inner.from_entry.to_owned(),
268            to_entry: self.inner.to_entry.to_owned(),
269            is_force: take(&mut self.inner.is_force),
270        }
271    }
272
273    impl_call_methods!(move_object, from_entry);
274}
275
276impl OperationProvider for MoveObjectBuilder<'_> {
277    fn to_operation(&mut self) -> String {
278        self.build().to_string()
279    }
280}
281
282impl Debug for MoveObjectBuilder<'_> {
283    #[inline]
284    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
285        f.debug_struct("MoveObjectBuilder").field("inner", &self.inner).finish()
286    }
287}
288
289#[derive(Clone, Debug)]
290pub(super) struct CopyObject<'a> {
291    from_entry: Entry<'a>,
292    to_entry: SimpleEntry<'a>,
293    is_force: bool,
294}
295
296impl CopyObject<'_> {
297    pub(super) fn builder<'a>(from_entry: Entry<'a>, to_bucket: &'a str, to_object: &'a str) -> CopyObjectBuilder<'a> {
298        CopyObjectBuilder {
299            inner: CopyObject {
300                from_entry,
301                to_entry: SimpleEntry::new(to_bucket, to_object),
302                is_force: false,
303            },
304            before_request_callback: None,
305        }
306    }
307
308    fn to_path_params(&self) -> qiniu_apis::storage::copy_object::PathParams {
309        qiniu_apis::storage::copy_object::PathParams::default()
310            .set_src_entry_as_str(self.from_entry.to_string())
311            .set_dest_entry_as_str(self.to_entry.to_string())
312            .set_is_force_as_bool(self.is_force)
313    }
314}
315
316impl Display for CopyObject<'_> {
317    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
318        write!(f, "copy/{}/{}", self.from_entry.encode(), self.to_entry.encode())?;
319        if self.is_force {
320            write!(f, "/force/true")?;
321        }
322        Ok(())
323    }
324}
325
326/// 对象复制操作构建器
327///
328/// 可以通过 [`crate::Bucket::copy_object_to`] 方法获取该构建器。
329#[derive(Clone)]
330pub struct CopyObjectBuilder<'a> {
331    inner: CopyObject<'a>,
332    before_request_callback: Option<BeforeRequestCallback<'a>>,
333}
334
335impl<'a> CopyObjectBuilder<'a> {
336    /// 是否强制复制
337    #[inline]
338    pub fn is_force(&mut self, is_force: bool) -> &mut Self {
339        self.inner.is_force = is_force;
340        self
341    }
342
343    /// 设置请求前回调函数
344    #[inline]
345    pub fn before_request_callback(
346        &mut self,
347        callback: impl FnMut(&mut RequestBuilderParts<'_>) -> AnyResult<()> + Send + Sync + 'a,
348    ) -> &mut Self {
349        self.before_request_callback = Some(Arc::new(Mutex::new(callback)));
350        self
351    }
352
353    fn build(&mut self) -> CopyObject<'a> {
354        CopyObject {
355            from_entry: self.inner.from_entry.to_owned(),
356            to_entry: self.inner.to_entry.to_owned(),
357            is_force: take(&mut self.inner.is_force),
358        }
359    }
360
361    impl_call_methods!(copy_object, from_entry);
362}
363
364impl OperationProvider for CopyObjectBuilder<'_> {
365    fn to_operation(&mut self) -> String {
366        self.build().to_string()
367    }
368}
369
370impl Debug for CopyObjectBuilder<'_> {
371    #[inline]
372    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
373        f.debug_struct("CopyObjectBuilder").field("inner", &self.inner).finish()
374    }
375}
376
377#[derive(Clone, Debug)]
378pub(super) struct DeleteObject<'a> {
379    entry: Entry<'a>,
380}
381
382impl DeleteObject<'_> {
383    pub(super) fn builder(entry: Entry) -> DeleteObjectBuilder {
384        DeleteObjectBuilder {
385            inner: DeleteObject { entry },
386            before_request_callback: None,
387        }
388    }
389
390    fn to_path_params(&self) -> qiniu_apis::storage::delete_object::PathParams {
391        qiniu_apis::storage::delete_object::PathParams::default().set_entry_as_str(self.entry.to_string())
392    }
393}
394
395impl Display for DeleteObject<'_> {
396    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
397        write!(f, "delete/{}", self.entry.encode())
398    }
399}
400
401/// 对象删除操作构建器
402///
403/// 可以通过 [`crate::Bucket::delete_object`] 方法获取该构建器。
404#[derive(Clone)]
405pub struct DeleteObjectBuilder<'a> {
406    inner: DeleteObject<'a>,
407    before_request_callback: Option<BeforeRequestCallback<'a>>,
408}
409
410impl<'a> DeleteObjectBuilder<'a> {
411    fn build(&mut self) -> DeleteObject<'a> {
412        self.inner.to_owned()
413    }
414
415    /// 设置请求前回调函数
416    #[inline]
417    pub fn before_request_callback(
418        &mut self,
419        callback: impl FnMut(&mut RequestBuilderParts<'_>) -> AnyResult<()> + Send + Sync + 'a,
420    ) -> &mut Self {
421        self.before_request_callback = Some(Arc::new(Mutex::new(callback)));
422        self
423    }
424
425    impl_call_methods!(delete_object);
426}
427
428impl OperationProvider for DeleteObjectBuilder<'_> {
429    fn to_operation(&mut self) -> String {
430        self.build().to_string()
431    }
432}
433
434impl Debug for DeleteObjectBuilder<'_> {
435    #[inline]
436    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
437        f.debug_struct("DeleteObjectBuilder")
438            .field("inner", &self.inner)
439            .finish()
440    }
441}
442
443#[derive(Clone, Debug)]
444pub(super) struct UnfreezeObject<'a> {
445    entry: Entry<'a>,
446    freeze_after_days: usize,
447}
448
449impl UnfreezeObject<'_> {
450    pub(super) fn builder(entry: Entry, freeze_after_days: usize) -> UnfreezeObjectBuilder {
451        UnfreezeObjectBuilder {
452            inner: UnfreezeObject {
453                entry,
454                freeze_after_days,
455            },
456            before_request_callback: None,
457        }
458    }
459
460    fn to_path_params(&self) -> qiniu_apis::storage::restore_archived_object::PathParams {
461        qiniu_apis::storage::restore_archived_object::PathParams::default()
462            .set_entry_as_str(self.entry.to_string())
463            .set_freeze_after_days_as_usize(self.freeze_after_days)
464    }
465}
466
467impl Display for UnfreezeObject<'_> {
468    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
469        write!(
470            f,
471            "restoreAr/{}/freezeAfterDays/{}",
472            self.entry.encode(),
473            self.freeze_after_days
474        )
475    }
476}
477
478/// 对象解冻操作构建器
479#[derive(Clone)]
480pub struct UnfreezeObjectBuilder<'a> {
481    inner: UnfreezeObject<'a>,
482    before_request_callback: Option<BeforeRequestCallback<'a>>,
483}
484
485impl<'a> UnfreezeObjectBuilder<'a> {
486    /// 设置请求前回调函数
487    #[inline]
488    pub fn before_request_callback(
489        &mut self,
490        callback: impl FnMut(&mut RequestBuilderParts<'_>) -> AnyResult<()> + Send + Sync + 'a,
491    ) -> &mut Self {
492        self.before_request_callback = Some(Arc::new(Mutex::new(callback)));
493        self
494    }
495
496    fn build(&mut self) -> UnfreezeObject<'a> {
497        self.inner.to_owned()
498    }
499
500    impl_call_methods!(restore_archived_object);
501}
502
503impl OperationProvider for UnfreezeObjectBuilder<'_> {
504    fn to_operation(&mut self) -> String {
505        self.build().to_string()
506    }
507}
508
509impl Debug for UnfreezeObjectBuilder<'_> {
510    #[inline]
511    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
512        f.debug_struct("UnfreezeObjectBuilder")
513            .field("inner", &self.inner)
514            .finish()
515    }
516}
517
518#[derive(Clone, Debug)]
519pub(super) struct SetObjectType<'a> {
520    entry: Entry<'a>,
521    object_type: FileType,
522}
523
524impl SetObjectType<'_> {
525    pub(super) fn builder(entry: Entry, object_type: FileType) -> SetObjectTypeBuilder {
526        SetObjectTypeBuilder {
527            inner: SetObjectType { entry, object_type },
528            before_request_callback: None,
529        }
530    }
531
532    fn to_path_params(&self) -> qiniu_apis::storage::set_object_file_type::PathParams {
533        qiniu_apis::storage::set_object_file_type::PathParams::default()
534            .set_entry_as_str(self.entry.to_string())
535            .set_type_as_usize(self.object_type.into())
536    }
537}
538
539impl Display for SetObjectType<'_> {
540    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
541        write!(f, "chtype/{}/type/{}", self.entry.encode(), self.object_type)
542    }
543}
544
545/// 对象类型设置操作构建器
546///
547/// 可以通过 [`crate::Bucket::set_object_type`] 方法获取该构建器。
548#[derive(Clone)]
549pub struct SetObjectTypeBuilder<'a> {
550    inner: SetObjectType<'a>,
551    before_request_callback: Option<BeforeRequestCallback<'a>>,
552}
553
554impl<'a> SetObjectTypeBuilder<'a> {
555    /// 设置请求前回调函数
556    #[inline]
557    pub fn before_request_callback(
558        &mut self,
559        callback: impl FnMut(&mut RequestBuilderParts<'_>) -> AnyResult<()> + Send + Sync + 'a,
560    ) -> &mut Self {
561        self.before_request_callback = Some(Arc::new(Mutex::new(callback)));
562        self
563    }
564
565    fn build(&mut self) -> SetObjectType<'a> {
566        self.inner.to_owned()
567    }
568
569    impl_call_methods!(set_object_file_type);
570}
571
572impl OperationProvider for SetObjectTypeBuilder<'_> {
573    fn to_operation(&mut self) -> String {
574        self.build().to_string()
575    }
576}
577
578impl Debug for SetObjectTypeBuilder<'_> {
579    #[inline]
580    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
581        f.debug_struct("SetObjectTypeBuilder")
582            .field("inner", &self.inner)
583            .finish()
584    }
585}
586
587#[derive(Clone, Debug)]
588pub(super) struct ModifyObjectStatus<'a> {
589    entry: Entry<'a>,
590    disabled: bool,
591}
592
593impl ModifyObjectStatus<'_> {
594    pub(super) fn builder(entry: Entry, disabled: bool) -> ModifyObjectStatusBuilder {
595        ModifyObjectStatusBuilder {
596            inner: ModifyObjectStatus { entry, disabled },
597            before_request_callback: None,
598        }
599    }
600
601    fn to_path_params(&self) -> qiniu_apis::storage::modify_object_status::PathParams {
602        qiniu_apis::storage::modify_object_status::PathParams::default()
603            .set_entry_as_str(self.entry.to_string())
604            .set_status_as_usize(usize::from(self.disabled))
605    }
606}
607
608impl Display for ModifyObjectStatus<'_> {
609    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
610        let disabled = if self.disabled { "1" } else { "0" };
611        write!(f, "chstatus/{}/status/{}", self.entry.encode(), disabled)
612    }
613}
614
615/// 修改对象状态构建器
616///
617/// 可以通过 [`crate::Bucket::modify_object_status`] 方法获取该构建器。
618#[derive(Clone)]
619pub struct ModifyObjectStatusBuilder<'a> {
620    inner: ModifyObjectStatus<'a>,
621    before_request_callback: Option<BeforeRequestCallback<'a>>,
622}
623
624impl<'a> ModifyObjectStatusBuilder<'a> {
625    /// 封禁对象
626    #[inline]
627    pub fn disable(&mut self, disable: bool) -> &mut Self {
628        self.inner.disabled = disable;
629        self
630    }
631
632    /// 设置请求前回调函数
633    #[inline]
634    pub fn before_request_callback(
635        &mut self,
636        callback: impl FnMut(&mut RequestBuilderParts<'_>) -> AnyResult<()> + Send + Sync + 'a,
637    ) -> &mut Self {
638        self.before_request_callback = Some(Arc::new(Mutex::new(callback)));
639        self
640    }
641
642    fn build(&mut self) -> ModifyObjectStatus<'a> {
643        ModifyObjectStatus {
644            entry: self.inner.entry.to_owned(),
645            disabled: take(&mut self.inner.disabled),
646        }
647    }
648
649    impl_call_methods!(modify_object_status);
650}
651
652impl OperationProvider for ModifyObjectStatusBuilder<'_> {
653    fn to_operation(&mut self) -> String {
654        self.build().to_string()
655    }
656}
657
658impl Debug for ModifyObjectStatusBuilder<'_> {
659    #[inline]
660    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
661        f.debug_struct("ModifyObjectStatusBuilder")
662            .field("inner", &self.inner)
663            .finish()
664    }
665}
666
667#[derive(Clone, Debug)]
668pub(super) struct ModifyObjectMetadata<'a> {
669    entry: Entry<'a>,
670    mime_type: Mime,
671    metadata: IndexMap<String, String>,
672    conditions: IndexMap<String, String>,
673}
674
675impl ModifyObjectMetadata<'_> {
676    pub(super) fn builder(entry: Entry, mime_type: Mime) -> ModifyObjectMetadataBuilder {
677        ModifyObjectMetadataBuilder {
678            inner: ModifyObjectMetadata {
679                entry,
680                mime_type,
681                metadata: Default::default(),
682                conditions: Default::default(),
683            },
684            before_request_callback: None,
685        }
686    }
687
688    fn to_path_params(&self) -> qiniu_apis::storage::modify_object_metadata::PathParams {
689        let mut params = qiniu_apis::storage::modify_object_metadata::PathParams::default()
690            .set_entry_as_str(self.entry.to_string())
691            .set_mime_type_as_str(self.mime_type.to_string());
692        if !self.conditions.is_empty() {
693            params = params.set_condition_as_str(self.condition_string());
694        }
695        for (key, value) in self.metadata.iter() {
696            params = params.append_meta_data_as_str(format!("x-qn-meta-{key}"), value.to_owned());
697        }
698        params
699    }
700
701    fn condition_string(&self) -> String {
702        let conditions: Vec<_> = self
703            .conditions
704            .iter()
705            .map(|(key, value)| format!("{key}={value}"))
706            .collect();
707        conditions.join("&")
708    }
709}
710
711impl Display for ModifyObjectMetadata<'_> {
712    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
713        write!(
714            f,
715            "chgm/{}/mime/{}",
716            self.entry.encode(),
717            urlsafe(self.mime_type.as_ref().as_bytes())
718        )?;
719        for (key, value) in self.metadata.iter() {
720            write!(f, "/x-qn-meta-{}/{}", key, urlsafe(value.as_bytes()))?;
721        }
722        if !self.conditions.is_empty() {
723            write!(f, "/cond/{}", urlsafe(self.condition_string().as_bytes()))?;
724        }
725        Ok(())
726    }
727}
728
729/// 修改对象元信息构建器
730///
731/// 可以通过 [`crate::Bucket::modify_object_metadata`] 方法获取该构建器。
732#[derive(Clone)]
733pub struct ModifyObjectMetadataBuilder<'a> {
734    inner: ModifyObjectMetadata<'a>,
735    before_request_callback: Option<BeforeRequestCallback<'a>>,
736}
737
738impl<'a> ModifyObjectMetadataBuilder<'a> {
739    /// 添加元信息
740    #[inline]
741    pub fn add_metadata(&mut self, key: impl Into<String>, value: impl Into<String>) -> &mut Self {
742        self.inner.metadata.insert(key.into(), value.into());
743        self
744    }
745
746    /// 添加修改条件条件
747    #[inline]
748    pub fn add_condition(&mut self, key: impl Into<String>, value: impl Into<String>) -> &mut Self {
749        self.inner.conditions.insert(key.into(), value.into());
750        self
751    }
752
753    /// 设置请求前回调函数
754    #[inline]
755    pub fn before_request_callback(
756        &mut self,
757        callback: impl FnMut(&mut RequestBuilderParts<'_>) -> AnyResult<()> + Send + Sync + 'a,
758    ) -> &mut Self {
759        self.before_request_callback = Some(Arc::new(Mutex::new(callback)));
760        self
761    }
762
763    fn build(&mut self) -> ModifyObjectMetadata<'a> {
764        ModifyObjectMetadata {
765            entry: self.inner.entry.to_owned(),
766            mime_type: self.inner.mime_type.to_owned(),
767            metadata: take(&mut self.inner.metadata),
768            conditions: take(&mut self.inner.conditions),
769        }
770    }
771
772    impl_call_methods!(modify_object_metadata);
773}
774
775impl OperationProvider for ModifyObjectMetadataBuilder<'_> {
776    fn to_operation(&mut self) -> String {
777        self.build().to_string()
778    }
779}
780
781impl Debug for ModifyObjectMetadataBuilder<'_> {
782    #[inline]
783    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
784        f.debug_struct("ModifyObjectMetadataBuilder")
785            .field("inner", &self.inner)
786            .finish()
787    }
788}
789
790#[derive(Clone, Debug)]
791pub(super) struct ModifyObjectLifeCycle<'a> {
792    entry: Entry<'a>,
793    to_ia_after_days: AfterDays,
794    to_archive_after_days: AfterDays,
795    to_deep_archive_after_days: AfterDays,
796    to_archive_ir_after_days: AfterDays,
797    delete_after_days: AfterDays,
798}
799
800impl ModifyObjectLifeCycle<'_> {
801    pub(super) fn builder(entry: Entry) -> ModifyObjectLifeCycleBuilder {
802        ModifyObjectLifeCycleBuilder {
803            inner: ModifyObjectLifeCycle {
804                entry,
805                to_ia_after_days: Default::default(),
806                to_archive_after_days: Default::default(),
807                to_deep_archive_after_days: Default::default(),
808                to_archive_ir_after_days: Default::default(),
809                delete_after_days: Default::default(),
810            },
811            before_request_callback: None,
812        }
813    }
814
815    fn to_path_params(&self) -> qiniu_apis::storage::modify_object_life_cycle::PathParams {
816        let mut params = qiniu_apis::storage::modify_object_life_cycle::PathParams::default()
817            .set_entry_as_str(self.entry.to_string());
818        if !self.to_ia_after_days.is_unmodified() {
819            params = params.set_to_ia_after_days_as_isize(self.to_ia_after_days.into());
820        }
821        if !self.to_archive_after_days.is_unmodified() {
822            params = params.set_to_archive_after_days_as_isize(self.to_archive_after_days.into());
823        }
824        if !self.to_deep_archive_after_days.is_unmodified() {
825            params = params.set_to_deep_archive_after_days_as_isize(self.to_deep_archive_after_days.into());
826        }
827        if !self.to_archive_ir_after_days.is_unmodified() {
828            params = params.set_to_archive_ir_after_days_as_isize(self.to_archive_ir_after_days.into());
829        }
830        if !self.delete_after_days.is_unmodified() {
831            params = params.set_delete_after_days_as_isize(self.delete_after_days.into());
832        }
833        params
834    }
835}
836
837impl Display for ModifyObjectLifeCycle<'_> {
838    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
839        write!(f, "lifecycle/{}", self.entry.encode())?;
840        if !self.to_ia_after_days.is_unmodified() {
841            write!(f, "/toIAAfterDays/{}", self.to_ia_after_days)?;
842        }
843        if !self.to_archive_after_days.is_unmodified() {
844            write!(f, "/toARCHIVEAfterDays/{}", self.to_archive_after_days)?;
845        }
846        if !self.to_deep_archive_after_days.is_unmodified() {
847            write!(f, "/toDeepArchiveAfterDays/{}", self.to_deep_archive_after_days)?;
848        }
849        if !self.to_archive_ir_after_days.is_unmodified() {
850            write!(f, "/toArchiveIRAfterDays/{}", self.to_archive_ir_after_days)?;
851        }
852        if !self.delete_after_days.is_unmodified() {
853            write!(f, "/deleteAfterDays/{}", self.delete_after_days)?;
854        }
855        Ok(())
856    }
857}
858
859/// 修改对象生命周期构建器
860///
861/// 可以通过 [`crate::Bucket::modify_object_life_cycle`] 方法获取该构建器。
862#[derive(Clone)]
863pub struct ModifyObjectLifeCycleBuilder<'a> {
864    inner: ModifyObjectLifeCycle<'a>,
865    before_request_callback: Option<BeforeRequestCallback<'a>>,
866}
867
868impl<'a> ModifyObjectLifeCycleBuilder<'a> {
869    /// 设置多少天后自动转换为低频文件
870    #[inline]
871    pub fn ia_after_days(&mut self, to_ia_after_days: AfterDays) -> &mut Self {
872        self.inner.to_ia_after_days = to_ia_after_days;
873        self
874    }
875
876    /// 设置多少天后自动转换为归档文件
877    #[inline]
878    pub fn archive_after_days(&mut self, to_archive_after_days: AfterDays) -> &mut Self {
879        self.inner.to_archive_after_days = to_archive_after_days;
880        self
881    }
882
883    /// 设置多少天后自动转换为深度归档文件
884    #[inline]
885    pub fn deep_archive_after_days(&mut self, to_deep_archive_after_days: AfterDays) -> &mut Self {
886        self.inner.to_deep_archive_after_days = to_deep_archive_after_days;
887        self
888    }
889
890    /// 设置多少天后自动转换为归档直读文件
891    #[inline]
892    pub fn archive_ir_after_days(&mut self, to_archive_ir_after_days: AfterDays) -> &mut Self {
893        self.inner.to_archive_ir_after_days = to_archive_ir_after_days;
894        self
895    }
896
897    /// 设置多少天后自动删除
898    #[inline]
899    pub fn delete_after_days(&mut self, to_delete_after_days: AfterDays) -> &mut Self {
900        self.inner.delete_after_days = to_delete_after_days;
901        self
902    }
903
904    /// 设置请求前回调函数
905    #[inline]
906    pub fn before_request_callback(
907        &mut self,
908        callback: impl FnMut(&mut RequestBuilderParts<'_>) -> AnyResult<()> + Send + Sync + 'a,
909    ) -> &mut Self {
910        self.before_request_callback = Some(Arc::new(Mutex::new(callback)));
911        self
912    }
913
914    fn build(&mut self) -> ModifyObjectLifeCycle<'a> {
915        ModifyObjectLifeCycle {
916            entry: self.inner.entry.to_owned(),
917            to_ia_after_days: take(&mut self.inner.to_ia_after_days),
918            to_archive_after_days: take(&mut self.inner.to_archive_after_days),
919            to_deep_archive_after_days: take(&mut self.inner.to_deep_archive_after_days),
920            to_archive_ir_after_days: take(&mut self.inner.to_archive_ir_after_days),
921            delete_after_days: take(&mut self.inner.delete_after_days),
922        }
923    }
924
925    impl_call_methods!(modify_object_life_cycle);
926}
927
928impl OperationProvider for ModifyObjectLifeCycleBuilder<'_> {
929    fn to_operation(&mut self) -> String {
930        self.build().to_string()
931    }
932}
933
934impl Debug for ModifyObjectLifeCycleBuilder<'_> {
935    #[inline]
936    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
937        f.debug_struct("ModifyObjectLifeCycleBuilder")
938            .field("inner", &self.inner)
939            .finish()
940    }
941}
942
943/// 设置对象生命周期天数
944#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
945pub struct AfterDays(isize);
946
947impl AfterDays {
948    /// 不设置生命周期
949    #[inline]
950    pub const fn unset() -> Self {
951        Self(-1)
952    }
953
954    /// 是否没有设置生命周期
955    #[inline]
956    pub const fn is_unset(self) -> bool {
957        self.0 == -1
958    }
959
960    /// 不修改生命周期
961    #[inline]
962    pub const fn unmodify() -> Self {
963        Self(0)
964    }
965
966    /// 是否不修改生命周期
967    #[inline]
968    pub const fn is_unmodified(self) -> bool {
969        self.0 == 0
970    }
971
972    /// 设置生命周期天数
973    #[inline]
974    pub const fn new(days: isize) -> Self {
975        Self(days)
976    }
977
978    /// 是否已经设置生命周期天数
979    #[inline]
980    pub const fn is_set(self) -> bool {
981        self.0 > 0
982    }
983
984    /// 获取生命周期的天数
985    #[inline]
986    pub const fn to_value(self) -> isize {
987        self.0
988    }
989}
990
991impl From<isize> for AfterDays {
992    #[inline]
993    fn from(num: isize) -> Self {
994        Self(num)
995    }
996}
997
998impl From<AfterDays> for isize {
999    #[inline]
1000    fn from(t: AfterDays) -> Self {
1001        t.0
1002    }
1003}
1004
1005impl Display for AfterDays {
1006    #[inline]
1007    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1008        Display::fmt(&self.0, f)
1009    }
1010}
1011
1012fn make_callback_error(err: AnyError) -> HttpResponseError {
1013    HttpResponseError::builder(HttpResponseErrorKind::CallbackError, err).build()
1014}
1015
1016#[cfg(test)]
1017#[cfg(feature = "async")]
1018mod tests {
1019    use super::{
1020        super::{mime, ObjectsManager},
1021        *,
1022    };
1023    use futures::future::BoxFuture;
1024    use qiniu_apis::{
1025        credential::Credential,
1026        http::{
1027            AsyncRequest, AsyncResponse, AsyncResponseBody, AsyncResponseResult, HeaderValue, HttpCaller, StatusCode,
1028            SyncRequest, SyncResponseResult,
1029        },
1030        http_client::{BucketName, DirectChooser, HttpClient, NeverRetrier, Region, NO_BACKOFF},
1031    };
1032    use serde_json::{json, to_vec as json_to_vec};
1033    use std::time::{SystemTime, UNIX_EPOCH};
1034
1035    #[async_std::test]
1036    async fn test_async_stat() -> anyhow::Result<()> {
1037        env_logger::builder().is_test(true).try_init().ok();
1038
1039        #[derive(Debug, Default)]
1040        struct FakeHttpCaller;
1041
1042        impl HttpCaller for FakeHttpCaller {
1043            fn call(&self, _request: &mut SyncRequest<'_>) -> SyncResponseResult {
1044                unreachable!()
1045            }
1046
1047            fn async_call<'a>(&'a self, request: &'a mut AsyncRequest<'_>) -> BoxFuture<'a, AsyncResponseResult> {
1048                Box::pin(async move {
1049                    assert!(request
1050                        .url()
1051                        .to_string()
1052                        .ends_with(&format!("/stat/{}", &encode_entry("fakeobjectname"))));
1053                    Ok(AsyncResponse::builder()
1054                        .status_code(StatusCode::OK)
1055                        .header("x-reqid", HeaderValue::from_static("FakeReqid"))
1056                        .body(AsyncResponseBody::from_bytes(
1057                            json_to_vec(&json!({
1058                                "fsize": 12345,
1059                                "hash": "fakehash",
1060                                "mimeType": "text/plain",
1061                                "type": 0,
1062                                "putTime": generate_put_time(),
1063                            }))
1064                            .unwrap(),
1065                        ))
1066                        .build())
1067                })
1068            }
1069        }
1070
1071        let bucket = get_bucket(FakeHttpCaller);
1072        let object = bucket.stat_object("fakeobjectname").async_call().await?.into_body();
1073        assert_eq!(object.get_hash_as_str(), "fakehash");
1074        assert_eq!(object.get_size_as_u64(), 12345u64);
1075
1076        Ok(())
1077    }
1078
1079    #[async_std::test]
1080    async fn test_async_copy() -> anyhow::Result<()> {
1081        env_logger::builder().is_test(true).try_init().ok();
1082
1083        #[derive(Debug, Default)]
1084        struct FakeHttpCaller;
1085
1086        impl HttpCaller for FakeHttpCaller {
1087            fn call(&self, _request: &mut SyncRequest<'_>) -> SyncResponseResult {
1088                unreachable!()
1089            }
1090
1091            fn async_call<'a>(&'a self, request: &'a mut AsyncRequest<'_>) -> BoxFuture<'a, AsyncResponseResult> {
1092                Box::pin(async move {
1093                    assert!(request.url().to_string().ends_with(&format!(
1094                        "/copy/{}/{}/force/true",
1095                        &encode_entry("fakeobjectname"),
1096                        &encode_entry("fakeobjectname2")
1097                    )));
1098                    Ok(AsyncResponse::builder()
1099                        .status_code(StatusCode::OK)
1100                        .header("x-reqid", HeaderValue::from_static("FakeReqid"))
1101                        .body(AsyncResponseBody::from_bytes(vec![]))
1102                        .build())
1103                })
1104            }
1105        }
1106
1107        let bucket = get_bucket(FakeHttpCaller);
1108        bucket
1109            .copy_object_to("fakeobjectname", &get_bucket_name(), "fakeobjectname2")
1110            .is_force(true)
1111            .async_call()
1112            .await?;
1113
1114        Ok(())
1115    }
1116
1117    #[async_std::test]
1118    async fn test_async_move() -> anyhow::Result<()> {
1119        env_logger::builder().is_test(true).try_init().ok();
1120
1121        #[derive(Debug, Default)]
1122        struct FakeHttpCaller;
1123
1124        impl HttpCaller for FakeHttpCaller {
1125            fn call(&self, _request: &mut SyncRequest<'_>) -> SyncResponseResult {
1126                unreachable!()
1127            }
1128
1129            fn async_call<'a>(&'a self, request: &'a mut AsyncRequest<'_>) -> BoxFuture<'a, AsyncResponseResult> {
1130                Box::pin(async move {
1131                    assert!(request.url().to_string().ends_with(&format!(
1132                        "/move/{}/{}/force/false",
1133                        &encode_entry("fakeobjectname"),
1134                        &encode_entry("fakeobjectname2")
1135                    )));
1136                    Ok(AsyncResponse::builder()
1137                        .status_code(StatusCode::OK)
1138                        .header("x-reqid", HeaderValue::from_static("FakeReqid"))
1139                        .body(AsyncResponseBody::from_bytes(vec![]))
1140                        .build())
1141                })
1142            }
1143        }
1144
1145        let bucket = get_bucket(FakeHttpCaller);
1146        bucket
1147            .move_object_to("fakeobjectname", &get_bucket_name(), "fakeobjectname2")
1148            .async_call()
1149            .await?;
1150
1151        Ok(())
1152    }
1153
1154    #[async_std::test]
1155    async fn test_async_delete() -> anyhow::Result<()> {
1156        env_logger::builder().is_test(true).try_init().ok();
1157
1158        #[derive(Debug, Default)]
1159        struct FakeHttpCaller;
1160
1161        impl HttpCaller for FakeHttpCaller {
1162            fn call(&self, _request: &mut SyncRequest<'_>) -> SyncResponseResult {
1163                unreachable!()
1164            }
1165
1166            fn async_call<'a>(&'a self, request: &'a mut AsyncRequest<'_>) -> BoxFuture<'a, AsyncResponseResult> {
1167                Box::pin(async move {
1168                    assert!(request
1169                        .url()
1170                        .to_string()
1171                        .ends_with(&format!("/delete/{}", &encode_entry("fakeobjectname"))));
1172                    Ok(AsyncResponse::builder()
1173                        .status_code(StatusCode::OK)
1174                        .header("x-reqid", HeaderValue::from_static("FakeReqid"))
1175                        .body(AsyncResponseBody::from_bytes(vec![]))
1176                        .build())
1177                })
1178            }
1179        }
1180
1181        let bucket = get_bucket(FakeHttpCaller);
1182        bucket.delete_object("fakeobjectname").async_call().await?;
1183
1184        Ok(())
1185    }
1186
1187    #[async_std::test]
1188    async fn test_async_unfreeze() -> anyhow::Result<()> {
1189        env_logger::builder().is_test(true).try_init().ok();
1190
1191        #[derive(Debug, Default)]
1192        struct FakeHttpCaller;
1193
1194        impl HttpCaller for FakeHttpCaller {
1195            fn call(&self, _request: &mut SyncRequest<'_>) -> SyncResponseResult {
1196                unreachable!()
1197            }
1198
1199            fn async_call<'a>(&'a self, request: &'a mut AsyncRequest<'_>) -> BoxFuture<'a, AsyncResponseResult> {
1200                Box::pin(async move {
1201                    assert!(request.url().to_string().ends_with(&format!(
1202                        "/restoreAr/{}/freezeAfterDays/7",
1203                        &encode_entry("fakeobjectname")
1204                    )));
1205                    Ok(AsyncResponse::builder()
1206                        .status_code(StatusCode::OK)
1207                        .header("x-reqid", HeaderValue::from_static("FakeReqid"))
1208                        .body(AsyncResponseBody::from_bytes(vec![]))
1209                        .build())
1210                })
1211            }
1212        }
1213
1214        let bucket = get_bucket(FakeHttpCaller);
1215        bucket.restore_archived_object("fakeobjectname", 7).async_call().await?;
1216
1217        Ok(())
1218    }
1219
1220    #[async_std::test]
1221    async fn test_async_set_type() -> anyhow::Result<()> {
1222        env_logger::builder().is_test(true).try_init().ok();
1223
1224        #[derive(Debug, Default)]
1225        struct FakeHttpCaller;
1226
1227        impl HttpCaller for FakeHttpCaller {
1228            fn call(&self, _request: &mut SyncRequest<'_>) -> SyncResponseResult {
1229                unreachable!()
1230            }
1231
1232            fn async_call<'a>(&'a self, request: &'a mut AsyncRequest<'_>) -> BoxFuture<'a, AsyncResponseResult> {
1233                Box::pin(async move {
1234                    assert!(request
1235                        .url()
1236                        .to_string()
1237                        .ends_with(&format!("/chtype/{}/type/2", &encode_entry("fakeobjectname"))));
1238                    Ok(AsyncResponse::builder()
1239                        .status_code(StatusCode::OK)
1240                        .header("x-reqid", HeaderValue::from_static("FakeReqid"))
1241                        .body(AsyncResponseBody::from_bytes(vec![]))
1242                        .build())
1243                })
1244            }
1245        }
1246
1247        let bucket = get_bucket(FakeHttpCaller);
1248        bucket
1249            .set_object_type("fakeobjectname", FileType::Archive)
1250            .async_call()
1251            .await?;
1252
1253        Ok(())
1254    }
1255
1256    #[async_std::test]
1257    async fn test_async_modify_status() -> anyhow::Result<()> {
1258        env_logger::builder().is_test(true).try_init().ok();
1259
1260        #[derive(Debug, Default)]
1261        struct FakeHttpCaller;
1262
1263        impl HttpCaller for FakeHttpCaller {
1264            fn call(&self, _request: &mut SyncRequest<'_>) -> SyncResponseResult {
1265                unreachable!()
1266            }
1267
1268            fn async_call<'a>(&'a self, request: &'a mut AsyncRequest<'_>) -> BoxFuture<'a, AsyncResponseResult> {
1269                Box::pin(async move {
1270                    assert!(request
1271                        .url()
1272                        .to_string()
1273                        .ends_with(&format!("/chstatus/{}/status/1", &encode_entry("fakeobjectname"))));
1274                    Ok(AsyncResponse::builder()
1275                        .status_code(StatusCode::OK)
1276                        .header("x-reqid", HeaderValue::from_static("FakeReqid"))
1277                        .body(AsyncResponseBody::from_bytes(vec![]))
1278                        .build())
1279                })
1280            }
1281        }
1282
1283        let bucket = get_bucket(FakeHttpCaller);
1284        bucket.modify_object_status("fakeobjectname", true).async_call().await?;
1285
1286        Ok(())
1287    }
1288
1289    #[async_std::test]
1290    async fn test_async_modify_metadata() -> anyhow::Result<()> {
1291        env_logger::builder().is_test(true).try_init().ok();
1292
1293        #[derive(Debug, Default)]
1294        struct FakeHttpCaller;
1295
1296        impl HttpCaller for FakeHttpCaller {
1297            fn call(&self, _request: &mut SyncRequest<'_>) -> SyncResponseResult {
1298                unreachable!()
1299            }
1300
1301            fn async_call<'a>(&'a self, request: &'a mut AsyncRequest<'_>) -> BoxFuture<'a, AsyncResponseResult> {
1302                Box::pin(async move {
1303                    assert!(request.url().to_string().ends_with(&format!(
1304                        "/chgm/{}/mime/{}/cond/{}/x-qn-meta-MetaKey-1/{}",
1305                        &encode_entry("fakeobjectname"),
1306                        &urlsafe(b"text/plain".as_slice()),
1307                        &urlsafe(b"hash=fakehash&mime=text/html".as_slice()),
1308                        &urlsafe(b"MetaValue-1".as_slice()),
1309                    )));
1310                    Ok(AsyncResponse::builder()
1311                        .status_code(StatusCode::OK)
1312                        .header("x-reqid", HeaderValue::from_static("FakeReqid"))
1313                        .body(AsyncResponseBody::from_bytes(vec![]))
1314                        .build())
1315                })
1316            }
1317        }
1318
1319        let bucket = get_bucket(FakeHttpCaller);
1320        bucket
1321            .modify_object_metadata("fakeobjectname", mime::TEXT_PLAIN)
1322            .add_metadata("MetaKey-1", "MetaValue-1")
1323            .add_condition("hash", "fakehash")
1324            .add_condition("mime", "text/html")
1325            .async_call()
1326            .await?;
1327
1328        Ok(())
1329    }
1330
1331    #[async_std::test]
1332    async fn test_async_modify_life_cycle() -> anyhow::Result<()> {
1333        env_logger::builder().is_test(true).try_init().ok();
1334
1335        #[derive(Debug, Default)]
1336        struct FakeHttpCaller;
1337
1338        impl HttpCaller for FakeHttpCaller {
1339            fn call(&self, _request: &mut SyncRequest<'_>) -> SyncResponseResult {
1340                unreachable!()
1341            }
1342
1343            fn async_call<'a>(&'a self, request: &'a mut AsyncRequest<'_>) -> BoxFuture<'a, AsyncResponseResult> {
1344                Box::pin(async move {
1345                    assert!(request.url().to_string().ends_with(&format!(
1346                        "/lifecycle/{}/toIAAfterDays/1/toArchiveAfterDays/2/toDeepArchiveAfterDays/3/toArchiveIRAfterDays/4/deleteAfterDays/5",
1347                        &encode_entry("fakeobjectname"),
1348                    )));
1349                    Ok(AsyncResponse::builder()
1350                        .status_code(StatusCode::OK)
1351                        .header("x-reqid", HeaderValue::from_static("FakeReqid"))
1352                        .body(AsyncResponseBody::from_bytes(vec![]))
1353                        .build())
1354                })
1355            }
1356        }
1357
1358        let bucket = get_bucket(FakeHttpCaller);
1359        bucket
1360            .modify_object_life_cycle("fakeobjectname")
1361            .ia_after_days(AfterDays::new(1))
1362            .archive_after_days(AfterDays::new(2))
1363            .deep_archive_after_days(AfterDays::new(3))
1364            .archive_ir_after_days(AfterDays::new(4))
1365            .delete_after_days(AfterDays::new(5))
1366            .async_call()
1367            .await?;
1368
1369        Ok(())
1370    }
1371
1372    fn get_bucket(caller: impl HttpCaller + 'static) -> Bucket {
1373        let object_manager = ObjectsManager::builder(get_credential())
1374            .http_client(
1375                HttpClient::builder(caller)
1376                    .chooser(DirectChooser)
1377                    .request_retrier(NeverRetrier)
1378                    .backoff(NO_BACKOFF)
1379                    .build(),
1380            )
1381            .build();
1382        object_manager.bucket_with_region(get_bucket_name(), single_rs_domain_region())
1383    }
1384
1385    fn get_credential() -> Credential {
1386        Credential::new("fakeaccesskey", "fakesecretkey")
1387    }
1388
1389    fn get_bucket_name() -> BucketName {
1390        "fakebucketname".into()
1391    }
1392
1393    fn encode_entry(object_name: &str) -> String {
1394        urlsafe(format!("{}:{}", get_bucket_name(), object_name).as_bytes())
1395    }
1396
1397    fn generate_put_time() -> u64 {
1398        SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_nanos() as u64 / 100
1399    }
1400
1401    fn single_rs_domain_region() -> Region {
1402        Region::builder("chaotic")
1403            .add_rs_preferred_endpoint(("fakers.example.com".to_owned(), 8080).into())
1404            .build()
1405    }
1406}