Repository navigation
Conversation
A schema with a discriminator and mapped children generated a serde internally-tagged enum whose variants were inline structs built from the parent's vars - every child-specific field was silently dropped, and with duplicate mappings the variants even mixed vars across models. Wrap the mapped model in a newtype variant instead (boxed, like the oneOf variants), named by the uniquified modelName while wrapping the model's real classname. serde's internally-tagged deserialization consumes the tag key, so a wrapped child's own required discriminator property would fail with "missing field": RustClientCodegen now marks mapped children's discriminator properties (the rust var context never set isDiscriminator) and the template defaults them, skipping them back out while empty so the tag stays the only occurrence on the wire. Optional discriminator properties already tolerate absence through Option and are untouched. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GcwZ1arjLZNpetHz2a3TJz
Review found the serde-attribute approach broke on a required nullable discriminator (String::is_empty on an Option<String> does not compile) and could duplicate the tag when a caller populated the child's field. Remove the property from mapped children instead, exactly as postProcessModels already removes it from the discriminating parent: the variant name carries the type information, deserialization never misses a consumed tag key, and the tag is structurally the only occurrence on the wire. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GcwZ1arjLZNpetHz2a3TJz
…de tag Removing the property from mapped children went too far: getMappedModels() covers every allOf descendant, and those models are also returned and accepted standalone (create_bar returns Bar, create_foo takes Foo), so they lost a required field and their new() signature changed. Keep the property declared and mark it instead, so the template defaults it - the internally-tagged union consumes the key before the child deserializes - and skips it back out while unset, keeping the tag the only occurrence on the wire. The skip predicate follows the type: Option::is_none for a nullable discriminator, String::is_empty for a non-nullable string, which is what the first attempt got wrong. Standalone use is unchanged: the caller sets and reads the discriminator as before. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GcwZ1arjLZNpetHz2a3TJz
serde's internally tagged enum consumes the tag before the wrapped child deserializes and writes it again next to the child's own discriminator field. The previous attempt defaulted and skipped that field on the child, which broke enum-typed discriminators (E0308 on `skip_serializing_if = "String::is_empty"`) and still wrote the tag twice for a child built with `new()`. The newtype unions now get a generated Serialize/Deserialize that goes through serde_json::Value: the child reads its own discriminator from the payload, and the variant's tag is written exactly once. Children are back to master (no attribute changes, no Java marking). A union whose mapping names the base itself has no struct to wrap and keeps master's inline variants. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…child" This reverts commit 881bd64. The hand-written Serialize/Deserialize through serde_json::Value tied the unions to JSON, doubled the (de)serialization work and did not round-trip nested unions. Unions derive #[serde(tag)] again. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
An enum-typed discriminator on a mapped child generated `skip_serializing_if = "String::is_empty"` on an enum field (E0308): it now gets `default` only. A union whose mapping names the base itself would wrap itself (E0275 once it is serialized): it keeps master's inline variants and its children are left untouched. Also mark only `vars` (the only list the template reads) and drop the unreachable classname fallback; shorter comments. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
With an enum-typed discriminator (inline or a $ref to an enum schema) the
newtype variant writes the tag twice on the way out, and the child's own
copy keeps the enum's default: {"petType":"Dog","bark":true} came back as
{"petType":"Dog","petType":"Cat","bark":true}. Flag those unions like the
self-mapped ones, so they keep master's inline variants and their children
are left unmarked, identical to master.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ted-union-child-fields # Conflicts: # modules/openapi-generator/src/test/java/org/openapitools/codegen/rust/RustClientCodegenTest.java
…ithub.com/wiebren/openapi-generator into wiebren-fix/rust-discriminated-union-child-fields
3 tasks done
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.
PR checklist
Commit all changed files.
This is important, as CI jobs will verify all generator outputs of your HEAD commit as it would merge with master.
These must match the expectations made by your contribution.
You may regenerate an individual generator by passing the relevant config(s) as an argument to the script, for example
./bin/generate-samples.sh bin/configs/java*.IMPORTANT: Do NOT purge/delete any folders/files (e.g. tests) when regenerating the samples as manually written tests may be removed.
Summary by cubic
Fixes the Rust generator silently dropping child-specific fields in discriminated unions. Mapped children now generate boxed newtype variants like
ObjectExists(Box<models::ObjectExists>)instead of inline structs built from the parent's vars.Behavior
rust reqwestsample and test spec covering nullability, enum-typed tags, and self-mapping.Written for commit 41564e5. Summary will update on new commits.