feat(client): typed query parameter serialization (form/deepObject styles) (#27)#28
Merged
Merged
Conversation
Query params with object schemas and form-explode semantics (the OAS 3.x defaults for in:query) previously mapped to Option<impl AsRef<str>> and were sent as one opaque filter=<string> pair. Per RFC 6570 form-explosion each object property must be its own query pair (?color=red&size=big). - analyzer: type such params as a struct — synthesized (e.g. FindWidgetsFilter) for inline object schemas, resolved for $refs — and mark them form_explode_object - client: emit req.query(&value) for marked params; reqwest serializes the struct via serde_urlencoded, one pair per set property, None fields omitted - deepObject and form+explode=false keep the string fallback (tracked in openapi-generator-anu); server codegen unchanged (openapi-generator-0jz) Closes #27 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Completes T14 for the client (openapi-generator-anu), building on the form-exploded object support from GH #27: - QuerySerialization enum on ParameterInfo replaces the form_explode_object bool; the analyzer assigns it from style/explode + schema shape per OAS 3.x defaults - form + explode=false objects: struct arg, one comma-joined ?filter=k1,v1,k2,v2 pair - deepObject objects: struct arg, bracketed ?filter[color]=red pairs (percent-encoded by reqwest; runtime-pinned in tests) - form arrays: Vec<T> args — explode=true repeats ?tags=a&tags=b, explode=false joins ?tags=a,b,c, empty vecs omitted; T maps scalars through the TypeMapper and $ref items to generated string enums (which emit Display) - still on the string fallback: deepObject arrays (undefined in OAS; stripe expand[]), space/pipeDelimited, arrays of objects - README gains a pre-1.0 breaking-changes log (user request): these are signature-level breaks on regeneration, replacing silently-wrong wire output; ship as 0.6.0 Server codegen and registry still see rust_type=String for all of these (openapi-generator-0jz). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Every scratch crate has an identical dependency tree, but each got its own target/, so reqwest/chrono/hyper/serde recompiled from scratch for all ~55 specs (~2-4 min each) — dwarfing the seconds it takes to check the generated code itself. Route the cargo-check step through a shared SPEC_COMPILE_TARGET_DIR (default tmp/spec-compile-target) that lives outside the per-run rm -rf: deps compile once ever, later runs and later specs start warm. The generator build keeps the workspace target/. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replaces 55 sequential cargo-check invocations (each re-resolving deps and taking the target-dir lock alone) with a single workspace check: one resolution, one lock, --keep-going, and cargo parallelizes the per-crate checks across cores itself. Per-spec attribution happens via cheap cached `-p` re-checks only when the workspace pass fails. Empirical: 3-spec run = 10s wall including dep bootstrap into the shared target dir (previously minutes per spec). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
lightsofapollo
added a commit
that referenced
this pull request
Jul 13, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #27, and completes the client half of T14 (beads
openapi-generator-anu).Query parameters with object or array schemas previously mapped to
Option<impl AsRef<str>>and went out as a single opaquename=<string>pair — wrong on the wire for every OAS style. Now the analyzer assigns each such parameter aQuerySerializationfrom itsstyle/explode+ schema shape, and the client generates the matching wire format:Option<Struct>?color=red&size=5Option<Struct>?filter=color,red,size,5Option<Struct>?filter[color]=redOption<Vec<T>>?tags=a&tags=bOption<Vec<T>>?tags=a,b,cStructis the referenced component model for$refschemas or a synthesized{Operation}{Param}struct for inline objects.Tmaps scalars through the TypeMapper or resolves$refitems to generated string enums (which emitDisplay); theArrayItemTypeenum keeps scalar type strings and raw schema names distinct so enum names sanitize throughto_rust_type_name(caught live: cloudflare'sresource-sharing_resource_type).Deliberately unchanged (still the opaque string passthrough): deepObject arrays (undefined in OAS — stripe's
expand[]),spaceDelimited/pipeDelimited, arrays of objects, and server-side extraction (openapi-generator-0jz).This is a breaking change by design — regenerated clients get new signatures replacing silently-wrong wire output. README now carries a pre-1.0 breaking-changes log, and this PR bumps the crate version to 0.6.0.
Also included:
scripts/spec-compile.shnow checks all scratch crates as one cargo workspace against a shared, run-survivingCARGO_TARGET_DIR— the full affected-spec sweep dropped from ~2-4 min/spec to a 29-spec pass in minutes.Verification
tests/exploded_query_params_test.rscovering every serialization mode, fallbacks, required/optional, coexistence — plus runtime pins proving reqwest emits?color=red&size=5, no dangling?for all-Nonestructs, and percent-encoded deepObject brackets.cargo checkcleanly (incl. cloudflare 2458 exploded arrays, microsoft-graph 13k comma-joined arrays, stripe/telnyx/modern-treasury deepObject filters).metadata[key]=valuedeepObject filters match their documented wire format.🤖 Generated with Claude Code