Skip to content

fix(types): emit working serde codecs for time::Date / time::Time (#25)#26

Merged
lightsofapollo merged 3 commits into
mainfrom
worktree-issue-25-time-date-serde
Jul 11, 2026
Merged

fix(types): emit working serde codecs for time::Date / time::Time (#25)#26
lightsofapollo merged 3 commits into
mainfrom
worktree-issue-25-time-date-serde

Conversation

@lightsofapollo

Copy link
Copy Markdown
Contributor

Fixes #25.

Problem

With DateStrategy::Time, format: date / format: time fields were emitted as

#[serde(with = "time::serde::iso8601")] // or ::option
pub proration_date: Option<time::Date>,

but time::serde::iso8601 only supports time::OffsetDateTime, so the generated code failed to compile (E0308, as reported).

Fix

The generated file now declares real codecs for these types via time::serde::format_description!, gated on actual usage (new TypeFeature::TimeDate / TimeTime, mirroring how base64_serde is gated):

time::serde::format_description!(time_date_format, Date, "[year]-[month]-[day]");
time::serde::format_description!(
    version = 2, time_time_format, Time,
    "[hour]:[minute]:[second][optional [.[subsecond]]]"
);

Fields then use with = "time_date_format" / "time_date_format::option" etc. The time format parses partial-time with or without fractional seconds; whole seconds serialize with a trailing .0 (valid RFC 3339, unavoidable since [optional] groups always format their contents).

Two latent bugs surfaced while verifying

  • REQUIRED_DEPS for time was wrong even before this: features = ["serde"] doesn't enable time::serde::rfc3339 (needs formatting/parsing). The emitted line is now time = { version = "0.3", features = ["serde", "formatting", "parsing", "macros"] } (macros only pulled in when date/time codecs are used), and collect_dep_requirements unions feature lists for the same crate instead of dropping duplicates.
  • Absent optional fields with a with codec failed to deserialize ("missing field") — a with attribute disables serde's implicit missing-field → None handling (Option fields require explicit default attribute if with attribute specified serde-rs/serde#2878). This also affected optional format: byte fields. Option-wrapped codec fields now also get #[serde(default)].

Testing

  • New regression tests in tests/typed_scalars_test.rs covering required/optional date, time, date-time (still rfc3339, no helper emitted), and the merged dep line.
  • Full cargo test suite passes.
  • End-to-end: generated code from the issue's exact shape (optional format: date), compiled it in a scratch crate against time 0.3.53, and round-tripped present, absent, and fractional-second values successfully.
  • scripts/spec-compile.sh smoke (anthropic/arcade/asana): all compile cleanly.

Side note: while testing I found that the README's documented [generator.types] TOML block is silently ignored — the parser reads top-level [types]. Filed as a separate beads issue (openapi-generator-cwd) since it's independent of this fix.

🤖 Generated with Claude Code

lightsofapollo and others added 3 commits July 11, 2026 12:48
DateStrategy::Time mapped `format: date` and `format: time` to
`#[serde(with = "time::serde::iso8601")]`, but that module only
supports OffsetDateTime — the generated code failed to compile
(#25).

The generator now declares per-type codec modules in the generated
file via `time::serde::format_description!`:

- `time_date_format`: "[year]-[month]-[day]" (RFC 3339 full-date)
- `time_time_format`: "[hour]:[minute]:[second][optional [.[subsecond]]]"
  (RFC 3339 partial-time; parses inputs with or without fractional
  seconds, serializes whole seconds with a trailing ".0")

Both come with an `::option` submodule, matching the existing
Option-field codec convention. Emission is gated on new
TypeFeature::TimeDate / TimeTime so specs without date/time fields
stay clean.

Two adjacent latent bugs fixed along the way:

- REQUIRED_DEPS listed `time = { features = ["serde"] }`, which
  doesn't even enable the rfc3339 codec; the time line now carries
  formatting/parsing (plus macros when date/time codecs are used),
  and collect_dep_requirements unions feature lists instead of
  dropping duplicates by crate name.
- Optional fields with a `with` codec (including format: byte)
  failed to deserialize when the field was absent — serde's
  missing-field → None shortcut is disabled by `with`
  (serde-rs/serde#2878). Such fields now also get #[serde(default)].

Verified end-to-end: generated the issue's exact shape (optional
format: date), compiled it against time 0.3, and round-tripped
present/absent/fractional values.

Closes #25

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@lightsofapollo lightsofapollo marked this pull request as ready for review July 11, 2026 18:54
@lightsofapollo lightsofapollo merged commit fe38905 into main Jul 11, 2026
5 checks passed
@lightsofapollo lightsofapollo deleted the worktree-issue-25-time-date-serde branch July 11, 2026 18:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TypeMapping for date to the Time module doesn't work

1 participant