Skip to content

[rust] fix: exploded deepObject free-form query parameters do not compile - #24899

Open
wiebren wants to merge 2 commits into
OpenAPITools:masterfrom
wiebren:fix/rust-deep-object-free-form
Open

[rust] fix: exploded deepObject free-form query parameters do not compile#24899
wiebren wants to merge 2 commits into
OpenAPITools:masterfrom
wiebren:fix/rust-deep-object-free-form

Conversation

@wiebren

@wiebren wiebren commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

A query parameter with style: deepObject, explode: true whose schema is a bare free-form
object (type: object, no additionalProperties) generates iteration code that cannot
compile: the exploded deepObject branch walks every map-flagged parameter with
.len()/.iter(), which exist on the HashMap a declared-additionalProperties schema
produces but not on the serde_json::Value a bare free-form schema produces.

This is the second of the two follow-ups promised in #24866's body ("the exploded
deepObject free-form branch iterates a serde_json::Value"); the first became #24896.

The fix

The deepObject {{#isMap}} branch splits on isContainer — exactly the flag that separates
the two typings (#24866 uses the same distinction). The HashMap shape keeps its direct
iteration; the serde_json::Value shape walks the value through as_object():

if let Some(object) = param_value.as_object() {
    let mut query_params = Vec::with_capacity(object.len());
    for (key, value) in object.iter() {
        query_params.push((key.to_string(), serde_json::to_string(value)?));
    }
    req_builder = req_builder.query(&query_params);
}

A non-object Value sends nothing, matching the parameter's declared object shape. Four
template sites: the nullable-required and optional paths in reqwest and reqwest-trait
(hyper has no deepObject-explode branch).

Tests

RustClientCodegenTest#testDeepObjectFreeFormQueryParamCompiles generates the new
3_0/rust/deep-object-free-form-query-param.yaml fixture (one deepObject parameter of each
shape) for both libraries and asserts the as_object() walk next to the direct map
iteration. Fails without the template change (verified by stashing it).

Verified by cargo build of clients generated from the fixture for both libraries: clean
with this change. Note the bare free-form parameter's type additionally needs #24866
(models::serde_json::Value today); the two changes touch different lines and compose —
the builds were verified with both applied.

PR checklist


Generated with Claude Code


Summary by cubic

Fixes Rust codegen for exploded deepObject query parameters with bare free-form object schemas, which previously generated uncompilable iteration code. Both reqwest and reqwest-trait now route exploded deepObject maps through parse_deep_object, which also changes typed maps to emit the name[key]=value wire format instead of json-quoted pairs.

Bug Fixes

  • Applies parse_deep_object to all four exploded deepObject map sites across both templates.
  • Adds a test fixture and generator test covering optional typed maps, optional free-form objects, and required-nullable maps in both libraries.

Written for commit 3590759. Summary will update on new commits.

Review in cubic

…pile

The exploded deepObject branch walks every map-flagged parameter with
.len()/.iter() - fine for a HashMap-typed map (an object schema with
declared additionalProperties), but a bare free-form object is a
serde_json::Value, which has neither, so the generated crate did not
compile. Split the branch on isContainer and walk the Value through
as_object(): a non-object Value sends nothing, matching the parameter's
declared object shape. The second of the two follow-ups promised in
OpenAPITools#24866's body.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GcwZ1arjLZNpetHz2a3TJz

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 4 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread modules/openapi-generator/src/main/resources/rust/reqwest-trait/api.mustache Outdated
Review pointed out the branch emitted unprefixed json-quoted pairs - not
the deepObject wire format the style asks for - and that was true of the
pre-existing HashMap iteration too. Both shapes (typed map and free-form
serde_json::Value) now take crate::apis::parse_deep_object, the route the
non-explode branch already uses: name[key]=value on the wire, one code
path, and the Value shape compiles because to_value accepts both. The
fixture gains a required-nullable parameter so all patched sites are
exercised, and the test registers the generated tree for cleanup.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GcwZ1arjLZNpetHz2a3TJz
@wiebren

wiebren commented Sep 8, 2026

Copy link
Copy Markdown
Contributor Author

cubic's P1 was right, and it applied to the pre-existing HashMap branch as much as to the new one: neither emitted deepObject form. Rather than patch the free-form copy, both shapes now route through crate::apis::parse_deep_object - the same call the non-explode branch already uses - so an exploded style: deepObject map reaches the wire as name[key]=value with plain string values, for the typed-map and serde_json::Value shapes alike, in one code path. Note this changes the wire output of the previously-compiling HashMap case from unprefixed json-quoted pairs to actual deepObject form; that output never matched the declared style, so this is the same fix, not a side effect.

The two test remarks are addressed too: the fixture gains a required-nullable deepObject parameter so every patched site is exercised, and the generated tree is registered for cleanup (files.forEach(File::deleteOnExit)).

Re-verified: cargo build clean for both libraries from the fixture (with #24866's typing fix applied on top for the bare free-form parameter's type), zero sample diffs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant