Derive Default on generated Rust types#1272
Conversation
When the @github/copilot schema gains an optional field on a wire type, every Rust test fixture that constructs that type by struct literal breaks until each one is hand-edited (cf. 70eb60e in #1270). Test fixtures shouldn't have to acknowledge new wire fields they don't care about. Teach the Rust codegen to derive `Default` on every generated struct and string-style enum (with `#[default]` on the `Unknown` catch-all where one exists), and propagate non-default-ness across structs that have a required field of an untagged-enum type (which has no obvious default variant). Also derive `Default` on `RequestId` for parity with `SessionId`. With this in place, the three internal Model fixtures can spell themselves with `..Default::default()` and survive future additive schema changes without manual intervention. Production code stays explicit: the rust-coding skill already forbids `..Default::default()` outside tests, and that rule is unchanged. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
main added explicit None initializers for the new model_picker_* fields; the merge layered those on top of our ..Default::default() making them redundant. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR updates the Rust code generator to derive Default on most generated wire-protocol types so Rust tests/fixtures can use ..Default::default() and remain resilient to additive schema changes (new optional fields), reducing churn on schema bumps.
Changes:
- Updated Rust codegen to derive
Defaultfor generated structs and for generated string/const-string enums (defaulting to the forward-compatUnknown/ sole variant), with an opt-out path for structs that require non-defaultable untagged-union types. - Regenerated Rust protocol types to include the new
Defaultderives and#[default]enum markers. - Updated a few Rust fixtures to use
..Default::default()and derivedDefaultforRequestIdfor parity withSessionId.
Show a summary per file
| File | Description |
|---|---|
| scripts/codegen/rust.ts | Adds Default derivation logic for generated structs/enums and tracks non-defaultable types to avoid invalid derives for untagged unions. |
| rust/src/generated/api_types.rs | Regenerated API wire types to derive Default across structs/enums. |
| rust/src/generated/session_events.rs | Regenerated session event wire types to derive Default across structs/enums (and SessionEventType). |
| rust/src/types.rs | Adds Default derive to RequestId. |
| rust/src/lib.rs | Updates internal test fixtures to use ..Default::default() for Model. |
| rust/tests/e2e/client.rs | Updates E2E test fixture Model construction to use ..Default::default(). |
Copilot's findings
- Files reviewed: 4/6 changed files
- Comments generated: 1
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
That all looks good, thanks! My one question was on this: Can you elaborate on that further? |
|
Worth spelling out the rationale for the two types in this PR that intentionally don't derive
This is also why I haven't reached for
|
…ults-in-tests # Conflicts: # rust/src/generated/api_types.rs
…ults-in-tests # Conflicts: # rust/src/generated/api_types.rs
When the
@github/copilotschema gains a new optional field on a wire type, every Rust test fixture that constructs that type with a struct literal breaks until each call site is hand-edited. #1270 needed exactly that fix in70eb60e2, and we'll hit it again on every schema bump. Test fixtures shouldn't have to acknowledge wire fields they don't care about.Approach
Teach the Rust codegen to derive
Defaulton the generated types so test fixtures can spell themselves with..Default::default()and ride out additive schema changes:Default, with one carve-out: structs that have a required field of a type with no sensible default (currently only untagged enums) skip the derive. The codegen tracks this in anonDefaultableTypesset and propagates transitively in a single depth-first pass.#[serde(other)] Unknown) deriveDefaultwith#[default]on theUnknownvariant.Defaultwith#[default]on their single variant.SessionEventType(hand-built in the codegen) gets the same treatment.RequestIdinrust/src/types.rsderivesDefaultfor parity withSessionId.The three internal
Modelfixtures inrust/src/lib.rsandrust/tests/e2e/client.rsare converted to..Default::default()to demonstrate and exercise the new derive.Notes for reviewers
#[non_exhaustive]is intentionally not part of this change. It's a separate decision (it does nothing inside our own crate, so it doesn't fix the internal-fixture pain — it's purely a downstream-facing SemVer instrument). Worth a follow-up on the open-world types...Default::default()outside tests, and that rule is unchanged. The intent here is only to make wire-type fixtures resilient to additive schema changes.DefaultonSessionEventData(the adjacent-tagged event payload enum) or onTypedSessionEvent(itsdatafield is non-default). Those are closed-set dispatch enums where consumers should get the compile-time signal when a new variant lands.cargo checkandcargo clippypass with the workspace's--features test-support -D warnings -D clippy::unwrap_used -D clippy::disallowed_macros -D clippy::await_holding_invalid_typesettings.cargo test --lib --features test-supportpasses (126/126). The fullcargo test --features test-supportrun hit failures in the e2e harness that look unrelated to this change (worth confirming in CI).