[rust] fix: discriminated-union variants drop every child-specific field

Merged
WI
1mo ago
+276-353

A schema with a discriminator and mapped children generates an internally tagged enum whose variants are inline structs built from the parent's vars, so every child-specific field is dropped (ObjectExists { message } loses identifier). The test-duplicates sample even mixed vars across models.

Fix

Newtype variants wrapping the mapped models, boxed like the oneOf variants, with the derived #[serde(tag)] kept:

ObjectExists(Box<models::ObjectExists>),

The variant is named by the uniquified modelName and wraps the model's real classname.

serde consumes the tag before the child deserializes, so a mapped child's discriminator field is marked and defaulted, and skipped while unset: Option::is_none if nullable, String::is_empty if a string. Standalone use of the child is unchanged.

Two kinds of union keep master's inline variants, children untouched:

  • a mapping that names the base itself would wrap itself (E0275);
  • an enum-typed discriminator (inline or $ref) would be written twice, the child's copy holding the enum's default ("petType":"Dog","petType":"Cat").

Verification

  • RustClientCodegenTest#testDiscriminatedUnionKeepsChildFields: string, nullable, inline-enum, enum-ref and self-mapped discriminators.
  • cargo check of the fixture; cargo run wire → union → wire keeps child fields and writes one tag. Enum-discriminated output is byte-identical to master.
  • Samples: oneOf and test-duplicates (hyper + reqwest) unions become newtypes; oneOf children gain the @type attribute. All build.

Known gaps

  • A child built with new() and wrapped in the union writes the tag twice ({"type":"ObjectExists","type":"ObjectExists",...}).
  • Self-mapped and enum-discriminated unions still drop their children's fields, as on master.

PR checklist

  • Read the contribution guidelines.
  • Built the project and updated samples (./bin/generate-samples.sh ./bin/configs/rust-*).
  • Technical committee (Rust): @frol @farcaller @richardwhiuk @paladinzh @jacob-pro @dsteeley

Summary by cubic

Fixes Rust client generation for schemas with a discriminator and mapped children so enum variants keep their child-specific fields. Variants were inline structs built from the parent's vars, silently dropping every field the child added; they now wrap the mapped model in a boxed newtype variant.

Behavior

  • Duplicate mapping entries sharing one schema still get distinct variants, each wrapping the real model.
  • The child's discriminator property stays declared but is marked as a discriminator: the consumed serde tag key defaults instead of erroring and is skipped back out while unset (Option::is_none for nullable, String::is_empty for non-nullable strings), keeping the tag the only occurrence on the wire.
  • Self-mapped and enum-typed discriminators keep the inline variants, so those unions still drop their children's fields; a child built with new() still writes the tag twice through the union.

Testing

  • Adds the discriminated-union.yaml fixture and a regression test pinning the derived #[serde(tag)].

Generated with Claude Code