Skip to content

fix: float precision and parameter-level x-enum-varnames - #42

Merged
lightsofapollo merged 2 commits into
mainfrom
fix/float-precision-and-param-enum-varnames
Jul 27, 2026
Merged

fix: float precision and parameter-level x-enum-varnames#42
lightsofapollo merged 2 commits into
mainfrom
fix/float-precision-and-param-enum-varnames

Conversation

@lightsofapollo

Copy link
Copy Markdown
Contributor

Stacked on #41 — the remaining three findings from live-testing a generated client against the RunPod v2 API. Base this on #41; merge that first.

format: float now maps to f64 (behavior change)

JSON carries no binary32. The declared format describes the server's storage, not the transport, so mapping it literally discards precision the response actually carried:

wire "0.03" -> f64  = 0.03
wire "0.03" -> f32  = 0.029999999329447746

This is a live observation, not a hypothetical: RunPod declares float on catalog prices and double on billing amounts, so the same money took two different precisions depending on which endpoint you called.

Filed under Changed rather than Fixed because it alters generated APIs. Opt out with float_precision = "f32" under [generator.types]; --types-conservative keeps the literal mapping. There's a test asserting the actual round-trip loss so the rationale in the docs can't silently rot.

Parameter-level inline enums honor x-enum-varnames

Schema-level enums already honored the extension through SchemaAnalysis::enum_extensions, but parameter enums are inline and have no analyzed-schema name to key on, so they silently fell back to the naming heuristic. An identical enum therefore produced different Rust variant names depending on whether it lived in components.schemas or on a parameter.

Plumbed through a new ParameterInfo::enum_varnames. A varnames array whose length disagrees with enum is ignored rather than applied to a prefix — the pairing would be a guess. Serde renames still target the wire value, and suffix disambiguation still applies.

problem_details() — intentionally unchanged

Reported as "can never succeed against this API," but that is correct behavior, not a defect.

The helper decodes the RFC 9457 profile, which is identified by Content-Type: application/problem+json. RunPod returns its errors as plain application/json, so None is the right answer. Loosening the media-type check would let arbitrary JSON error bodies be misinterpreted as Problem Details — a worse bug than the one reported. Servers generated by this tool always emit the problem media type, so it works against them.

The real gap was discoverability, so the generated doc comment now explains why third-party APIs yield None and points at typed / body instead. Happy to add opportunistic parsing behind a flag if that's wanted.

Verification

  • 369 tests pass
  • cargo clippy --all-features -- -D warnings clean, cargo fmt clean
  • Full corpus: 54 passed, 0 gen-failed, 0 check-failed, 1 skipped (gitea, Swagger 2.0). Run locally, since the float change touches every spec with a numeric field and the per-PR tier only compile-checks OpenAI and Anthropic.

One pre-existing test asserted format: floatf32 for query parameters; updated to f64 with the rationale inline, since that is precisely the behavior being corrected.

🤖 Generated with Claude Code

The remaining findings from live-testing a generated client against the
RunPod v2 API.

- `format: float` now maps to f64 rather than f32. JSON carries no binary32,
  so the declared format describes the server's storage, not the transport: a
  price sent as 0.03 survives in f64 but becomes 0.029999999329447746 through
  f32. RunPod declares float on catalog prices and double on billing, so the
  same money took two different precisions depending on endpoint. Opt out with
  float_precision = "f32"; --types-conservative keeps the literal mapping.
- Parameter-level inline enums honor x-enum-varnames. Schema-level enums
  already did, so an identical enum produced different Rust variant names
  depending on whether it lived in components.schemas or on a parameter. A
  varnames array whose length disagrees with enum is ignored rather than
  applied to a prefix, since the pairing would be a guess.

problem_details() is deliberately unchanged. It decodes the RFC 9457 profile,
identified by Content-Type: application/problem+json; RunPod returns plain
application/json, so None is correct. Loosening the check would misparse
arbitrary JSON error bodies as Problem Details. Its doc comment now explains
why third-party APIs yield None and points at `typed`/`body` instead.

Verified: 369 tests pass, clippy and fmt clean, and the full 54-spec corpus
generates and compiles against its own dependency fragments.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 27, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
openapi-to-rust Ready Ready Preview, Comment Jul 27, 2026 3:38am

Request Review

full-spec-compile has been failing with SIGTERM (143) roughly 25 minutes into
a 240-minute budget, on main as well as on branches, since well before the
current fixes. It is not a timeout and not disk.

Measured cause: microsoft-graph generates 2.4M lines (16,153 operations) and
peaks at ~14.3 GB RSS in a single rustc process during cargo check. A hosted
ubuntu-latest runner has 16 GB, so it is killed. CARGO_BUILD_JOBS=1 changes
the peak by 0.1% — the memory is one rustc type-checking one crate, so
parallelism and sharding do not help.

Adds GENERATE_ONLY_SPECS, currently just microsoft-graph. It is still
generated, which is where most generator defects surface; only the compile
step is skipped. SPEC_COMPILE_FORCE_CHECK=1 runs it anyway where there is
headroom.

Generate-only specs are counted separately and never folded into `passed`,
and the success line degrades to "N compiled cleanly; M generated but not
compiled" — a green run must not read as full verification when one spec was
never compiled.

Full corpus: 53 passed, 0 gen-failed, 0 check-failed, 1 generate-only,
1 skipped (gitea, Swagger 2.0).

Refs: openapi-generator-lnj

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@lightsofapollo
lightsofapollo changed the base branch from fix/live-tested-generator-defects to main July 27, 2026 03:37
@lightsofapollo
lightsofapollo merged commit 7a2d752 into main Jul 27, 2026
22 checks passed
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