diff --git a/src/storage/examples/Cargo.toml b/src/storage/examples/Cargo.toml index 63d46e47ab..3f0a69c29d 100644 --- a/src/storage/examples/Cargo.toml +++ b/src/storage/examples/Cargo.toml @@ -23,6 +23,9 @@ repository.workspace = true keywords.workspace = true categories.workspace = true +[lints] +workspace = true + [dependencies] anyhow.workspace = true bytes.workspace = true diff --git a/src/storage/examples/src/lib.rs b/src/storage/examples/src/lib.rs index 326cd119d4..54e67c8e53 100644 --- a/src/storage/examples/src/lib.rs +++ b/src/storage/examples/src/lib.rs @@ -521,6 +521,77 @@ pub async fn run_object_examples(buckets: &mut Vec) -> anyhow::Result<() objects::object_csek_to_cmek::sample(&control, &id, "csek_file.txt", new_csek_key, &kms_key) .await?; + #[cfg(google_cloud_unstable_storage_bidi)] + { + use google_cloud_test_utils::runtime_config::{region_id, zone_id}; + tracing::info!("create rapid bucket for appendable examples"); + let rapid_bucket_id = random_bucket_id(); + buckets.push(rapid_bucket_id.clone()); + let _ = control + .create_bucket() + .set_parent("projects/_") + .set_bucket_id(rapid_bucket_id.clone()) + .set_bucket( + Bucket::new() + .set_project(format!("projects/{project_id}")) + .set_location(region_id()) + .set_custom_placement_config( + CustomPlacementConfig::new().set_data_locations([zone_id()]), + ) + .set_storage_class("RAPID") + .set_hierarchical_namespace(HierarchicalNamespace::new().set_enabled(true)) + .set_iam_config(IamConfig::new().set_uniform_bucket_level_access( + UniformBucketLevelAccess::new().set_enabled(true), + )), + ) + .send() + .await?; + + tracing::info!("running open_appendable_object_write example"); + objects::open_appendable_object_write::sample( + &client, + &rapid_bucket_id, + "appendable-write", + ) + .await?; + + tracing::info!("running open_appendable_object_pause_resume example"); + objects::open_appendable_object_pause_resume::sample( + &client, + &rapid_bucket_id, + "appendable-pause-resume", + ) + .await?; + + let mut writer = client + .open_appendable_object( + format!("projects/_/buckets/{rapid_bucket_id}"), + "appendable-finalize", + ) + .send() + .await?; + writer.append(bytes::Bytes::from("hello ")).await?; + let generation = writer.generation(); + writer.close().await?; + + tracing::info!("running open_appendable_object_finalize example"); + objects::open_appendable_object_finalize::sample( + &client, + &rapid_bucket_id, + "appendable-finalize", + generation, + ) + .await?; + + tracing::info!("running open_appendable_object_read_tail example"); + objects::open_appendable_object_read_tail::sample( + &client, + &rapid_bucket_id, + "appendable-write", + ) + .await?; + } + tracing::info!("create bucket for object ACL, retention examples"); let id = random_bucket_id(); buckets.push(id.clone()); @@ -548,6 +619,7 @@ pub async fn run_object_examples(buckets: &mut Vec) -> anyhow::Result<() objects::remove_file_owner::sample(&control, &id, &service_account).await?; tracing::info!("running set_object_retention_policy example"); objects::set_object_retention_policy::sample(&control, &id).await?; + Ok(()) } diff --git a/src/storage/examples/src/objects.rs b/src/storage/examples/src/objects.rs index ac19f9f211..3efbf0ace6 100644 --- a/src/storage/examples/src/objects.rs +++ b/src/storage/examples/src/objects.rs @@ -41,6 +41,14 @@ pub mod list_object_contexts; pub mod make_public; pub mod move_file; pub mod object_csek_to_cmek; +#[cfg(google_cloud_unstable_storage_bidi)] +pub mod open_appendable_object_finalize; +#[cfg(google_cloud_unstable_storage_bidi)] +pub mod open_appendable_object_pause_resume; +#[cfg(google_cloud_unstable_storage_bidi)] +pub mod open_appendable_object_read_tail; +#[cfg(google_cloud_unstable_storage_bidi)] +pub mod open_appendable_object_write; pub mod open_multiple_objects_ranged_read; pub mod open_object_multiple_ranged_read; pub mod open_object_read_full_object; diff --git a/src/storage/examples/src/objects/open_appendable_object_finalize.rs b/src/storage/examples/src/objects/open_appendable_object_finalize.rs new file mode 100644 index 0000000000..6b484179f7 --- /dev/null +++ b/src/storage/examples/src/objects/open_appendable_object_finalize.rs @@ -0,0 +1,34 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// [START storage_open_appendable_object_finalize] +use google_cloud_storage::client::Storage; + +pub async fn sample( + client: &Storage, + bucket: &str, + object: &str, + generation: i64, +) -> Result<(), anyhow::Error> { + let writer = client + .reopen_appendable_object(format!("projects/_/buckets/{bucket}"), object, generation) + .send() + .await?; + + let metadata = writer.finalize().await?; + + println!("Finalized appendable object {object} in bucket {bucket}: {metadata:?}"); + Ok(()) +} +// [END storage_open_appendable_object_finalize] diff --git a/src/storage/examples/src/objects/open_appendable_object_pause_resume.rs b/src/storage/examples/src/objects/open_appendable_object_pause_resume.rs new file mode 100644 index 0000000000..33076a3c84 --- /dev/null +++ b/src/storage/examples/src/objects/open_appendable_object_pause_resume.rs @@ -0,0 +1,40 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// [START storage_open_appendable_object_pause_resume] +use bytes::Bytes; +use google_cloud_storage::client::Storage; + +pub async fn sample(client: &Storage, bucket: &str, object: &str) -> Result<(), anyhow::Error> { + let mut writer = client + .open_appendable_object(format!("projects/_/buckets/{bucket}"), object) + .send() + .await?; + + writer.append(Bytes::from("Part 1")).await?; + let generation = writer.generation(); + writer.close().await?; + + let mut resumed_writer = client + .reopen_appendable_object(format!("projects/_/buckets/{bucket}"), object, generation) + .send() + .await?; + + resumed_writer.append(Bytes::from("Part 2")).await?; + let metadata = resumed_writer.finalize().await?; + + println!("Appended data across multiple sessions to {object} in bucket {bucket}: {metadata:?}"); + Ok(()) +} +// [END storage_open_appendable_object_pause_resume] diff --git a/src/storage/examples/src/objects/open_appendable_object_read_tail.rs b/src/storage/examples/src/objects/open_appendable_object_read_tail.rs new file mode 100644 index 0000000000..7ab85397ae --- /dev/null +++ b/src/storage/examples/src/objects/open_appendable_object_read_tail.rs @@ -0,0 +1,49 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// [START storage_open_appendable_object_read_tail] +use bytes::Bytes; +use google_cloud_storage::client::Storage; +use google_cloud_storage::model_ext::ReadRange; +use std::time::Duration; +use tokio::time::sleep; + +pub async fn sample(client: &Storage, bucket: &str, object: &str) -> Result<(), anyhow::Error> { + let mut writer = client + .open_appendable_object(format!("projects/_/buckets/{bucket}"), object) + .send() + .await?; + + let mut bytes_read: u64 = 0; + for i in 0..2 { + let content = format!("More data for tail example, iteration {i}\n"); + writer.append(Bytes::from(content)).await?; + writer.flush().await?; + + let (_metadata, mut reader) = client + .open_object(format!("projects/_/buckets/{bucket}"), object) + .send_and_read(ReadRange::offset(bytes_read)) + .await?; + + while let Some(chunk) = reader.next().await.transpose()? { + print!("{}", String::from_utf8_lossy(&chunk)); + bytes_read += chunk.len() as u64; + } + + sleep(Duration::from_secs(1)).await; + } + + Ok(()) +} +// [END storage_open_appendable_object_read_tail] diff --git a/src/storage/examples/src/objects/open_appendable_object_write.rs b/src/storage/examples/src/objects/open_appendable_object_write.rs new file mode 100644 index 0000000000..008a54a199 --- /dev/null +++ b/src/storage/examples/src/objects/open_appendable_object_write.rs @@ -0,0 +1,32 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// [START storage_open_appendable_object_write] +use bytes::Bytes; +use google_cloud_storage::client::Storage; + +pub async fn sample(client: &Storage, bucket: &str, object: &str) -> Result<(), anyhow::Error> { + let mut writer = client + .open_appendable_object(format!("projects/_/buckets/{bucket}"), object) + .send() + .await?; + + writer.append(Bytes::from("Hello ")).await?; + writer.append(Bytes::from("World!")).await?; + let metadata = writer.finalize().await?; + + println!("Appended data to {object} in bucket {bucket}: {metadata:?}"); + Ok(()) +} +// [END storage_open_appendable_object_write]