feat: distinguishes missing properties, explicit null, and values - #133
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
The lowering currently can produce nested Nullable<Nullable<T>> wrappers when nullable is applied redundantly, which diverges from intended semantics and should be normalized.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
This PR updates the Rust generator to accurately represent (and round-trip) the three distinct states of JSON fields: missing, explicit null, and present non-null value. It introduces a Nullable<T> wrapper for nullable schemas, updates lowering/emission to propagate nullability through types/constraints, and expands fixtures/tests/docs to cover the new semantics.
Changes:
- Introduce
Nullable<T>as the Rust representation for OpenAPInullable: true, including for inline request/response bodies, and propagate it through lowering passes (rename/prune/recurse/direction). - Change optional-field deserialization to distinguish missing (
None) from explicit null (error for non-nullable optionals;Some(Nullable::Null)for optional-nullables) via generateddeserialize_withshims. - Expand coverage diagnostics, fixtures, and generated-code tests for nullable presence/nullability behavior and name-collision handling.
File summaries
| File | Description |
|---|---|
| examples/bookstore/generated/restapi/operations/list_books.rs | Regenerated query parsing to validate optionals on deserialize (distinguish missing vs explicit null). |
| examples/bookstore/generated/apimodel/catalog.rs | Regenerated models to validate optional fields on deserialize. |
| docs/design.md | Document new presence/nullability matrix and default/null limitations. |
| crates/oapi-codegen/tests/generated/value_constraints.rs | Updated expected generated validation for optional fields. |
| crates/oapi-codegen/tests/generated/type_name_collisions.rs | Updated expected generated validation for optional fields. |
| crates/oapi-codegen/tests/generated/server_xfile_refs.rs | Updated expected generated query parsing/validation for optionals. |
| crates/oapi-codegen/tests/generated/server_query_params.rs | Updated expected generated query parsing/validation for optionals. |
| crates/oapi-codegen/tests/generated/server_petstore.rs | Updated expected generated model parsing/validation for optionals. |
| crates/oapi-codegen/tests/generated/server_multi_content_request.rs | Updated expected generated model parsing/validation for optionals. |
| crates/oapi-codegen/tests/generated/server_component_param_ref.rs | Updated expected generated query parsing/validation for optionals. |
| crates/oapi-codegen/tests/generated/schema_defaults.rs | Updated expected defaults and introduces Nullable<T> in generated output expectations. |
| crates/oapi-codegen/tests/generated/recursive_schema.rs | Updated expected generated validation for optionals in recursive structures. |
| crates/oapi-codegen/tests/generated/read_write_only.rs | Updated expected generated validation for optionals and wrapped nullable recursion. |
| crates/oapi-codegen/tests/generated/prelude_value_names.rs | Updated expected generated validation for optionals (prelude-name stress case). |
| crates/oapi-codegen/tests/generated/prelude_result_name.rs | Updated expected generated validation for optionals (prelude-name stress case). |
| crates/oapi-codegen/tests/generated/package_nullable/server/wrapped.rs | New generated-package server fixture for nullable request/response bodies. |
| crates/oapi-codegen/tests/generated/package_nullable/server/parity.rs | New generated-package server fixture for nullable array bodies. |
| crates/oapi-codegen/tests/generated/package_nullable/server/exchange.rs | New generated-package server fixture for directional nullable models. |
| crates/oapi-codegen/tests/generated/package_nullable/server.rs | New generated-package server entrypoint for nullable fixture. |
| crates/oapi-codegen/tests/generated/package_nullable/operations/wrapped.rs | New generated-package operation types for nullable fixture. |
| crates/oapi-codegen/tests/generated/package_nullable/operations/parity.rs | New generated-package operation types for nullable fixture. |
| crates/oapi-codegen/tests/generated/package_nullable/operations/exchange.rs | New generated-package operation types for nullable fixture. |
| crates/oapi-codegen/tests/generated/package_nullable/operations.rs | New generated-package operations module for nullable fixture. |
| crates/oapi-codegen/tests/generated/package_nullable/models.rs | New generated-package models module including Nullable<T> helper and aliases. |
| crates/oapi-codegen/tests/generated/package_nullable/client/wrapped.rs | New generated-package client fixture for nullable bodies. |
| crates/oapi-codegen/tests/generated/package_nullable/client/parity.rs | New generated-package client fixture for nullable array bodies. |
| crates/oapi-codegen/tests/generated/package_nullable/client/exchange.rs | New generated-package client fixture for directional nullable models. |
| crates/oapi-codegen/tests/generated/package_nullable/client.rs | New generated-package client entrypoint for nullable fixture. |
| crates/oapi-codegen/tests/generated/package_nullable.rs | New generated-package crate root module for nullable fixture. |
| crates/oapi-codegen/tests/generated/package_combined_server_client/models.rs | Updated expected combined output for optional validation shims. |
| crates/oapi-codegen/tests/generated/package_combined_response_name_collision/models.rs | Updated expected combined output for optional validation shims. |
| crates/oapi-codegen/tests/generated/package_combined_prelude_value_names/operations/probe.rs | Updated expected combined output for optional query validation shims. |
| crates/oapi-codegen/tests/generated/package_combined_prelude_value_names/models.rs | Updated expected combined output for optional validation shims. |
| crates/oapi-codegen/tests/generated/package_combined_inline_nullable/server/scalar.rs | New combined+package server fixture for inline nullable bodies. |
| crates/oapi-codegen/tests/generated/package_combined_inline_nullable/server/map.rs | New combined+package server fixture for inline nullable bodies. |
| crates/oapi-codegen/tests/generated/package_combined_inline_nullable/server/array.rs | New combined+package server fixture for inline nullable bodies. |
| crates/oapi-codegen/tests/generated/package_combined_inline_nullable/server.rs | New combined+package server entrypoint for inline nullable bodies. |
| crates/oapi-codegen/tests/generated/package_combined_inline_nullable/operations/scalar.rs | New combined+package operation types for inline nullable bodies. |
| crates/oapi-codegen/tests/generated/package_combined_inline_nullable/operations/map.rs | New combined+package operation types for inline nullable bodies. |
| crates/oapi-codegen/tests/generated/package_combined_inline_nullable/operations/array.rs | New combined+package operation types for inline nullable bodies. |
| crates/oapi-codegen/tests/generated/package_combined_inline_nullable/operations.rs | New combined+package operations module for inline nullable bodies. |
| crates/oapi-codegen/tests/generated/package_combined_inline_nullable/models.rs | New combined+package models module including Nullable<T> helper. |
| crates/oapi-codegen/tests/generated/package_combined_inline_nullable/client/scalar.rs | New combined+package client fixture for inline nullable scalar body. |
| crates/oapi-codegen/tests/generated/package_combined_inline_nullable/client/map.rs | New combined+package client fixture for inline nullable map body. |
| crates/oapi-codegen/tests/generated/package_combined_inline_nullable/client/array.rs | New combined+package client fixture for inline nullable array body. |
| crates/oapi-codegen/tests/generated/package_combined_inline_nullable/client.rs | New combined+package client entrypoint for inline nullable bodies. |
| crates/oapi-codegen/tests/generated/package_combined_inline_nullable.rs | New combined+package crate root module for inline nullable bodies. |
| crates/oapi-codegen/tests/generated/object_optional_required.rs | Updated expected generated validation for optional fields. |
| crates/oapi-codegen/tests/generated/object_nested_inline.rs | Updated expected generated validation for optional fields. |
| crates/oapi-codegen/tests/generated/object_deny_unknown_fields.rs | Updated expected generated validation for optional fields under deny_unknown_fields. |
| crates/oapi-codegen/tests/generated/nullable.rs | Updated expected generated output for nullable presence/nullability and new helper. |
| crates/oapi-codegen/tests/generated/integer_enum.rs | Updated expected generated validation for optional fields. |
| crates/oapi-codegen/tests/generated/ext_vendor_extensions.rs | Updated expected generated validation for optional fields and nullable keep-null semantics. |
| crates/oapi-codegen/tests/generated/combined_server_client.rs | Updated expected combined output for optional validation shims. |
| crates/oapi-codegen/tests/generated/combined_response_name_collision.rs | Updated expected combined output for optional validation shims. |
| crates/oapi-codegen/tests/generated/combined_prelude_value_names.rs | Updated expected combined output for optional validation shims. |
| crates/oapi-codegen/tests/generated/combined_inline_nullable.rs | New combined-output fixture for inline nullable bodies. |
| crates/oapi-codegen/tests/generated.rs | Add runtime tests for nullable round-trips and form/JSON behaviors. |
| crates/oapi-codegen/tests/fixtures/nullable.yaml | Expand nullable fixture with bodies and richer schema coverage. |
| crates/oapi-codegen/tests/fixtures/nullable_rename_collision.yaml | New fixture for nullable helper name collisions via x-rust-name. |
| crates/oapi-codegen/tests/fixtures/nullable_name_collision.yaml | New fixture for nullable helper name collisions with schema named Nullable. |
| crates/oapi-codegen/tests/fixtures/combined_inline_nullable.yaml | New fixture for inline nullable bodies in combined output. |
| crates/oapi-codegen/tests/coverage.rs | Register new features/fixtures and add helper-name collision assertions. |
| crates/oapi-codegen/src/lower/schema.rs | Lower nullable named schemas into Nullable<T> aliases + Value types; propagate nullable in type lowering. |
| crates/oapi-codegen/src/lower/rename.rs | Ensure rename pass traverses RustType::Nullable. |
| crates/oapi-codegen/src/lower/recurse.rs | Ensure boxing pass traverses RustType::Nullable. |
| crates/oapi-codegen/src/lower/prune.rs | Ensure pruning discovers named refs through RustType::Nullable. |
| crates/oapi-codegen/src/lower/paths.rs | Support nullable inline body schemas and single-member allOf bodies; map empty object bodies to maps; wrap nullable bodies. |
| crates/oapi-codegen/src/lower/direction.rs | Ensure direction projection traverses RustType::Nullable. |
| crates/oapi-codegen/src/lower/default.rs | Add detection for unsupported nullable defaults and adjust default lowering for nullable. |
| crates/oapi-codegen/src/lower/constraints.rs | Handle nullable constraints/default limitations and “only-null-valid” cases without hard errors. |
| crates/oapi-codegen/src/lib.rs | Reserve Nullable only when emitted/used; tighten collision checks accordingly. |
| crates/oapi-codegen/src/ir.rs | Add RustType::Nullable to IR and update helper methods. |
| crates/oapi-codegen/src/emit/usage.rs | Ensure usage analysis traverses RustType::Nullable. |
| crates/oapi-codegen/src/emit/package.rs | Pass nullable-helper emission flag into package model emission. |
| crates/oapi-codegen/src/emit/models.rs | Emit validate fns for Option<T> fields (missing vs null) and support nullable defaults. |
| crates/oapi-codegen/src/emit/mod.rs | Emit Nullable<T> helper type conditionally and render RustType::Nullable. |
| crates/oapi-codegen/src/emit/constraints.rs | Update validation emission to unwrap checks through Nullable/Option and to reject null for non-nullable optionals. |
| crates/oapi-codegen/src/coverage.rs | Update diagnostics to warn on discarded default: null and nullable limitations in non-JSON wires. |
Review details
- Files reviewed: 23/81 changed files
- Comments generated: 2
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
There was a problem hiding this comment.
🔵 Needs a closer look
It makes broad, cross-cutting semantic changes to nullable/presence handling across lowering, IR, emission, and many fixtures, so it warrants final human review for subtle serde/OpenAPI edge cases.
Review details
- Files reviewed: 23/84 changed files
- Comments generated: 0 new
- Review effort level: Lite
|
🎉 This PR is included in version 1.3.0-dev.2 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
|
🎉 This PR is included in version 1.3.0 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
No description provided.