From 01be87637d067ba400cef44f7cd6676c852533de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Olav=20L=C3=B8ite?= Date: Sat, 1 Aug 2026 11:56:13 +0200 Subject: [PATCH] feat(gax): add mutable extension accessors to RequestOptions Add `get_extension_mut` and `get_extension_or_default_mut` to the `RequestOptionsExt` trait and implement them for `RequestOptions`. These methods allow client library layers to mutate request option extensions in place (such as injecting or updating headers in an existing `HeaderMap`) without needing to clone or re-insert the extension. --- Cargo.lock | 2 +- Cargo.toml | 2 +- librarian.yaml | 2 +- src/gax/Cargo.toml | 2 +- src/gax/src/options.rs | 59 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 63 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9ceb88c055..67beac1075 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3153,7 +3153,7 @@ dependencies = [ [[package]] name = "google-cloud-gax" -version = "1.13.0" +version = "1.14.0" dependencies = [ "anyhow", "bytes", diff --git a/Cargo.toml b/Cargo.toml index abbf1a2c6f..1c87d26227 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -511,7 +511,7 @@ tokio-stream = { default-features = false, version = "0.1.16" } # Local packages used as dependencies. google-cloud-auth = { default-features = false, version = "1.15.0", path = "src/auth" } -google-cloud-gax = { default-features = false, version = "1.13.0", path = "src/gax" } +google-cloud-gax = { default-features = false, version = "1.14.0", path = "src/gax" } gaxi = { default-features = false, version = "0.7.16", path = "src/gax-internal", package = "google-cloud-gax-internal" } wkt = { default-features = false, version = "1.7.0", path = "src/wkt", package = "google-cloud-wkt" } google-cloud-wkt = { default-features = false, version = "1.7.0", path = "src/wkt", package = "google-cloud-wkt" } diff --git a/librarian.yaml b/librarian.yaml index cde4b7b9d6..72f717b53c 100644 --- a/librarian.yaml +++ b/librarian.yaml @@ -889,7 +889,7 @@ libraries: version: 1.13.0 copyright_year: "2025" - name: google-cloud-gax - version: 1.13.0 + version: 1.14.0 copyright_year: "2025" output: src/gax - name: google-cloud-gax-internal diff --git a/src/gax/Cargo.toml b/src/gax/Cargo.toml index a536d70dc3..744a639821 100644 --- a/src/gax/Cargo.toml +++ b/src/gax/Cargo.toml @@ -18,7 +18,7 @@ name = "google-cloud-gax" # version of all downstream dependencies. For details see: # https://github.com/googleapis/google-cloud-rust/issues/3237 # https://github.com/googleapis/google-cloud-rust/issues/3265 -version = "1.13.0" +version = "1.14.0" description = "Google Cloud Client Libraries for Rust" # Inherit other attributes from the workspace. authors.workspace = true diff --git a/src/gax/src/options.rs b/src/gax/src/options.rs index d25f61c7b5..d8a4b469db 100644 --- a/src/gax/src/options.rs +++ b/src/gax/src/options.rs @@ -265,6 +265,16 @@ pub mod internal { where T: Send + Sync + 'static; + /// Gets a mutable reference to an extension value. + fn get_extension_mut(&mut self) -> Option<&mut T> + where + T: Send + Sync + 'static; + + /// Gets a mutable reference to an extension value, inserting the default if it does not exist. + fn get_extension_or_default_mut(&mut self) -> &mut T + where + T: Default + Clone + Send + Sync + 'static; + /// Sets an extension value. fn insert_extension(self, value: T) -> Self where @@ -280,6 +290,25 @@ pub mod internal { self.extensions.get::() } + fn get_extension_mut(&mut self) -> Option<&mut T> + where + T: Send + Sync + 'static, + { + self.extensions.get_mut::() + } + + fn get_extension_or_default_mut(&mut self) -> &mut T + where + T: Default + Clone + Send + Sync + 'static, + { + if self.extensions.get::().is_none() { + let _ = self.extensions.insert(T::default()); + } + self.extensions + .get_mut::() + .expect("value was just inserted if missing") + } + fn insert_extension(mut self, value: T) -> Self where T: Clone + Send + Sync + 'static, @@ -466,6 +495,36 @@ mod tests { assert_eq!(opts.get_extension::(), Some(&TestB(42)), "{opts:?}"); } + #[test] + fn request_options_ext_mut() { + #[derive(Debug, Clone, Default, PartialEq)] + struct TestCounter(u32); + + let mut opts = RequestOptions::default(); + + // 1. get_extension_mut returns None when not present. + assert!(opts.get_extension_mut::().is_none()); + + // 2. get_extension_or_default_mut inserts default TestCounter(0) and returns mutable reference. + let counter = opts.get_extension_or_default_mut::(); + assert_eq!(counter, &mut TestCounter(0)); + counter.0 += 10; + + // 3. get_extension_mut returns Some(&mut TestCounter(10)) when present. + let counter = opts + .get_extension_mut::() + .expect("counter extension should be present after insertion"); + assert_eq!(counter, &mut TestCounter(10)); + counter.0 += 10; + + // 4. Second call to get_extension_or_default_mut returns existing reference without resetting. + let counter2 = opts.get_extension_or_default_mut::(); + assert_eq!(counter2, &mut TestCounter(20)); + counter2.0 += 5; + + assert_eq!(opts.get_extension::(), Some(&TestCounter(25))); + } + #[test] fn request_options_builder() -> anyhow::Result<()> { const USER_AGENT: &str = "test-only";