Skip to content

feat: distinguishes missing properties, explicit null, and values - #133

Merged
dotkas merged 2 commits into
developfrom
dotkas/oapi-presence-nullability
Sep 7, 2026
Merged

dotkas merged 2 commits into
developfrom
dotkas/oapi-presence-nullability

Conversation

@dotkas

@dotkas dotkas commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

No description provided.

@dotkas
dotkas marked this pull request as ready for review September 7, 2026 08:44
Copilot AI lite review requested due to automatic review settings September 7, 2026 08:44

Copilot AI 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.

🟡 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 OpenAPI nullable: 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 generated deserialize_with shims.
  • 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.

Comment thread crates/oapi-codegen/src/lower/paths.rs
Comment thread crates/oapi-codegen/src/lower/schema.rs
Copilot AI review requested due to automatic review settings September 7, 2026 17:55

Copilot AI 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.

🔵 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

@dotkas
dotkas merged commit 87e9e25 into develop Sep 7, 2026
8 checks passed
@dotkas
dotkas deleted the dotkas/oapi-presence-nullability branch September 7, 2026 18:12
@alchemax-housekeeper

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 1.3.0-dev.2 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

@alchemax-housekeeper

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 1.3.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants