diff --git a/.gitignore b/.gitignore index 22d35163..f1116fbc 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,6 @@ Cargo.lock # Added by cargo /target + +# IDE +.idea diff --git a/storage/Cargo.toml b/storage/Cargo.toml index 163fa68a..a90c7daa 100644 --- a/storage/Cargo.toml +++ b/storage/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT" name = "google-cloud-storage" readme = "README.md" repository = "https://github.com/yoshidan/google-cloud-rust/tree/main/storage" -version = "0.20.0" +version = "0.21.0" [dependencies] anyhow = "1.0" diff --git a/storage/src/http/objects/mod.rs b/storage/src/http/objects/mod.rs index 8ebf03d5..07d91ea1 100644 --- a/storage/src/http/objects/mod.rs +++ b/storage/src/http/objects/mod.rs @@ -12,6 +12,8 @@ pub mod delete; pub mod download; pub mod get; pub mod list; + +pub mod r#move; pub mod patch; pub mod rewrite; pub mod upload; diff --git a/storage/src/http/objects/move.rs b/storage/src/http/objects/move.rs new file mode 100644 index 00000000..0cd88696 --- /dev/null +++ b/storage/src/http/objects/move.rs @@ -0,0 +1,89 @@ +use reqwest_middleware::{ClientWithMiddleware as Client, RequestBuilder}; + +use crate::http::objects::copy::CopyObjectRequest; +use crate::http::objects::delete::DeleteObjectRequest; +use crate::http::objects::{Encryption, Object}; +use crate::http::{object_access_controls::Projection, Escape}; + +/// Request message for moving an object. +#[derive(Clone, PartialEq, Eq, serde::Deserialize, serde::Serialize, Debug, Default)] +#[serde(rename_all = "camelCase")] +pub struct MoveObjectRequest { + /// Name of the new object. Required when the object metadata is not otherwise provided. Overrides the object metadata's name value, if any. + pub destination_bucket: String, + /// Name of the new object. Required when the object metadata is not otherwise provided. Overrides the object metadata's name value, if any. + pub destination_object: String, + // Name of the source object. For information about how to URL encode object names to be path safe, see Encoding URI path parts. + pub source_object: String, + /// Name of the bucket in which to find the source object. + #[serde(skip_serializing)] + pub source_bucket: String, + + /// Makes the operation conditional on there being a live destination object with a generation number that matches the given value. Setting ifGenerationMatch to 0 makes the operation succeed only if there is no live destination object. + pub if_generation_match: Option, + /// Makes the operation conditional on there being a live destination object with a generation number that does not match the given value. If no live destination object exists, the precondition fails. Setting ifGenerationNotMatch to 0 makes the operation succeed if there is a live version of the object. + pub if_generation_not_match: Option, + /// Makes the operation conditional on there being a live destination object with a metageneration number that matches the given value. + pub if_metageneration_match: Option, + /// Makes the operation conditional on there being a live destination object with a metageneration number that does not match the given value. + pub if_metageneration_not_match: Option, + /// Makes the operation conditional on whether the source object's generation matches the given value. + pub if_source_generation_match: Option, + /// Makes the operation conditional on whether the source object's generation does not match the given value. + pub if_source_generation_not_match: Option, + /// Makes the operation conditional on whether the source object's current metageneration matches the given value. + pub if_source_metageneration_match: Option, + /// Makes the operation conditional on whether the source object's current metageneration does not match the given value. + pub if_source_metageneration_not_match: Option, + /// Set of properties to return. Defaults to noAcl, unless the object resource specifies the acl property, when it defaults to full. + /// + /// Acceptable values are: + /// full: Include all properties. + /// noAcl: Omit the owner, acl property. + pub projection: Option, + /// If present, selects a specific revision of the source object (as opposed to the latest version, the default) + pub source_generation: Option, + /// The Object metadata for updating. + #[serde(skip_serializing)] + pub metadata: Option, + + #[serde(skip_serializing)] + pub encryption: Option, +} + +impl From for CopyObjectRequest { + fn from(value: MoveObjectRequest) -> Self { + CopyObjectRequest { + destination_bucket: value.destination_bucket, + destination_object: value.destination_object, + source_object: value.source_object, + source_bucket: value.source_bucket, + if_generation_match: value.if_generation_match, + if_generation_not_match: value.if_generation_not_match, + if_metageneration_match: value.if_metageneration_match, + if_metageneration_not_match: value.if_metageneration_not_match, + if_source_generation_match: value.if_source_generation_match, + if_source_generation_not_match: value.if_source_generation_not_match, + if_source_metageneration_match: value.if_source_metageneration_match, + if_source_metageneration_not_match: value.if_source_metageneration_not_match, + projection: value.projection, + source_generation: value.source_generation, + metadata: value.metadata, + encryption: value.encryption, + } + } +} + +impl From for DeleteObjectRequest { + fn from(value: MoveObjectRequest) -> Self { + DeleteObjectRequest { + bucket: value.source_bucket, + object: value.source_object, + generation: value.source_generation, + if_generation_match: value.if_source_generation_match, + if_generation_not_match: value.if_source_generation_not_match, + if_metageneration_match: value.if_metageneration_match, + if_metageneration_not_match: value.if_metageneration_not_match, + } + } +} diff --git a/storage/src/http/storage_client.rs b/storage/src/http/storage_client.rs index 939f08f9..13b288e1 100644 --- a/storage/src/http/storage_client.rs +++ b/storage/src/http/storage_client.rs @@ -53,6 +53,7 @@ use crate::http::objects::download::Range; use crate::http::objects::get::GetObjectRequest; use crate::http::objects::list::{ListObjectsRequest, ListObjectsResponse}; use crate::http::objects::patch::PatchObjectRequest; +use crate::http::objects::r#move::MoveObjectRequest; use crate::http::objects::rewrite::{RewriteObjectRequest, RewriteObjectResponse}; use crate::http::objects::upload::{UploadObjectRequest, UploadType}; use crate::http::objects::Object; @@ -930,6 +931,35 @@ impl StorageClient { self.send(builder).await } + /// Move the object. + /// https://cloud.google.com/storage/docs/copying-renaming-moving-objects#rest-move-object + /// This function will first make a copy of the object, and then delete the original object. + /// + /// ``` + /// use google_cloud_storage::client::Client; + /// use google_cloud_storage::http::objects::r#move::MoveObjectRequest; + /// + /// async fn run(client:Client) { + /// let result = client.move_object(&MoveObjectRequest{ + /// source_bucket: "source_bucket".to_string(), + /// source_object: "source_object".to_string(), + /// destination_bucket: "destination_source".to_string(), + /// destination_object: "destination_object".to_string(), + /// ..Default::default() + /// }).await; + /// } + /// ``` + /// + #[cfg_attr(feature = "trace", tracing::instrument(skip_all))] + pub async fn move_object(&self, req: &MoveObjectRequest) -> Result { + let copy_req: CopyObjectRequest = req.clone().into(); + let delete_req: DeleteObjectRequest = req.clone().into(); + // Only result of the copy operations is of interest, as it contains details of destination. + let copy_result = self.copy_object(©_req).await?; + let _ = self.delete_object(&delete_req).await?; + Ok(copy_result) + } + /// Download the object. /// https://cloud.google.com/storage/docs/json_api/v1/objects/get /// alt is always media @@ -2195,7 +2225,7 @@ pub(crate) mod test { status1, UploadStatus::ResumeIncomplete(UploadedRange { first_byte: 0, - last_byte: chunk1_data.len() as u64 - 1 + last_byte: chunk1_data.len() as u64 - 1, }) ); @@ -2205,7 +2235,7 @@ pub(crate) mod test { status_check, UploadStatus::ResumeIncomplete(UploadedRange { first_byte: 0, - last_byte: chunk1_data.len() as u64 - 1 + last_byte: chunk1_data.len() as u64 - 1, }) ); @@ -2311,7 +2341,7 @@ pub(crate) mod test { status1, UploadStatus::ResumeIncomplete(UploadedRange { first_byte: 0, - last_byte: chunk1_data.len() as u64 - 1 + last_byte: chunk1_data.len() as u64 - 1, }) ); @@ -2325,7 +2355,7 @@ pub(crate) mod test { status1, UploadStatus::ResumeIncomplete(UploadedRange { first_byte: 0, - last_byte: chunk1_data.len() as u64 - 1 + last_byte: chunk1_data.len() as u64 - 1, }) );