Lower bare type: object to an open map - #1
Open
4t145 wants to merge 1 commit into
Open
Conversation
A `type: object` schema with no declared `properties` was classified as
`Shape::Object` and lowered through `build_struct`, emitting a fieldless
`pub struct Foo {}`. That type drops every key on serialize and carries
nothing on deserialize, so a client holding one has no way to send the
real payload — the array case is worse, since `items: {type: object}`
hoists a named `pub struct XxxItems {}` and the whole array becomes
unusable.
JSON Schema defaults `additionalProperties` to `true`, so such a schema
in fact accepts any object. Classify it as `Shape::Map` instead, which
already lowers to `BTreeMap<String, serde_json::Value>`.
`additionalProperties: false` stays on the struct path: with no declared
properties it genuinely describes the empty object and nothing else, so
the fieldless struct is the faithful lowering.
Adds regression tests for the bare object, the array-of-bare-objects
shape that surfaced this, the closed-empty exception, and an object with
properties as a control.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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
A
type: objectschema with no declaredpropertieswas classified asShape::Objectand lowered throughbuild_struct, emitting a fieldlesspub struct Foo {}. That type drops every key on serialize and carries nothing on deserialize, so a client holding one has no way to send the real payload.The array case is what surfaced this. Given:
the generator hoisted
pub struct XxxReferences {}and producedVec<XxxReferences>— an array the caller can only fill with empty objects, silently discarding whatever they meant to send.JSON Schema defaults
additionalPropertiestotrue, so a baretype: objectin fact accepts any object. It is now classified asShape::Map, which already lowers toBTreeMap<String, serde_json::Value>.additionalProperties: falsestays on the struct path: with no declared properties it genuinely describes the empty object and nothing else, so the fieldless struct is the faithful lowering.Version bumped to 0.1.3, following the two prior codegen fixes.
Before / after
type: objectpub struct Opaque {}pub type Opaque = BTreeMap<String, Value>type: object,additionalProperties: falsepub struct Sentinel {}type: objectwithpropertiespub struct User { .. }arrayofitems: {type: object}Vec<XxxItems>(empty struct)Vec<BTreeMap<String, Value>>arrayofitems: {}Vec<Value>Test plan
cargo test -p toac-build -p toac— all 22 test targets pass, including the petstore 3.1 and e2b real-spec snapshot tests, so no existing lowering shiftedcargo clippy -p toac-build -p toac --all-targets— no warningscargo fmt— cleantoac-build/tests/test_bare_object_schemas.rs, 4 cases: bare object → open map, array-of-bare-objects keeps the payload reachable,additionalProperties: falsekeeps its struct, object-with-properties as a controlclassify()fails the 3 behavioural cases and leaves the control passingNot covered: no test asserts the generated code round-trips a real payload through serde. The assertions are on the emitted type, not on wire behaviour.
🤖 Generated with Claude Code