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 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 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#[clonable]
129#[auto_impl(&mut, Box)]
130pub trait OperationProvider: Clone + Debug + Sync + Send {
131 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#[derive(Clone)]
163pub struct StatObjectBuilder<'a> {
164 inner: StatObject<'a>,
165 before_request_callback: Option<BeforeRequestCallback<'a>>,
166}
167
168impl<'a> StatObjectBuilder<'a> {
169 #[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#[derive(Clone)]
242pub struct MoveObjectBuilder<'a> {
243 inner: MoveObject<'a>,
244 before_request_callback: Option<BeforeRequestCallback<'a>>,
245}
246
247impl<'a> MoveObjectBuilder<'a> {
248 #[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 #[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#[derive(Clone)]
330pub struct CopyObjectBuilder<'a> {
331 inner: CopyObject<'a>,
332 before_request_callback: Option<BeforeRequestCallback<'a>>,
333}
334
335impl<'a> CopyObjectBuilder<'a> {
336 #[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 #[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#[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 #[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#[derive(Clone)]
480pub struct UnfreezeObjectBuilder<'a> {
481 inner: UnfreezeObject<'a>,
482 before_request_callback: Option<BeforeRequestCallback<'a>>,
483}
484
485impl<'a> UnfreezeObjectBuilder<'a> {
486 #[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#[derive(Clone)]
549pub struct SetObjectTypeBuilder<'a> {
550 inner: SetObjectType<'a>,
551 before_request_callback: Option<BeforeRequestCallback<'a>>,
552}
553
554impl<'a> SetObjectTypeBuilder<'a> {
555 #[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#[derive(Clone)]
619pub struct ModifyObjectStatusBuilder<'a> {
620 inner: ModifyObjectStatus<'a>,
621 before_request_callback: Option<BeforeRequestCallback<'a>>,
622}
623
624impl<'a> ModifyObjectStatusBuilder<'a> {
625 #[inline]
627 pub fn disable(&mut self, disable: bool) -> &mut Self {
628 self.inner.disabled = disable;
629 self
630 }
631
632 #[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#[derive(Clone)]
733pub struct ModifyObjectMetadataBuilder<'a> {
734 inner: ModifyObjectMetadata<'a>,
735 before_request_callback: Option<BeforeRequestCallback<'a>>,
736}
737
738impl<'a> ModifyObjectMetadataBuilder<'a> {
739 #[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 #[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 #[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#[derive(Clone)]
863pub struct ModifyObjectLifeCycleBuilder<'a> {
864 inner: ModifyObjectLifeCycle<'a>,
865 before_request_callback: Option<BeforeRequestCallback<'a>>,
866}
867
868impl<'a> ModifyObjectLifeCycleBuilder<'a> {
869 #[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 #[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 #[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 #[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 #[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 #[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#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
945pub struct AfterDays(isize);
946
947impl AfterDays {
948 #[inline]
950 pub const fn unset() -> Self {
951 Self(-1)
952 }
953
954 #[inline]
956 pub const fn is_unset(self) -> bool {
957 self.0 == -1
958 }
959
960 #[inline]
962 pub const fn unmodify() -> Self {
963 Self(0)
964 }
965
966 #[inline]
968 pub const fn is_unmodified(self) -> bool {
969 self.0 == 0
970 }
971
972 #[inline]
974 pub const fn new(days: isize) -> Self {
975 Self(days)
976 }
977
978 #[inline]
980 pub const fn is_set(self) -> bool {
981 self.0 > 0
982 }
983
984 #[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}