Skip to content

Runtime (Rust): expand ResultFormatter to full TS parity (24 new methods)#5

Open
kevinelliott wants to merge 1 commit into
mainfrom
feat/rust-runtime-formatters
Open

Runtime (Rust): expand ResultFormatter to full TS parity (24 new methods)#5
kevinelliott wants to merge 1 commit into
mainfrom
feat/rust-runtime-formatters

Conversation

@kevinelliott

@kevinelliott kevinelliott commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Summary

Mirrors the C runtime expansion (#4) into ads_runtime::ResultFormatter — but this time with every type/code/label/value string cross-checked against the production TS reference (runtimes/typescript/utils/result_formatter.ts). That verification surfaced several wrong codes in both the original Rust formatters and PR #4's C versions.

Corrections to existing methods (TS-verified)

Method Was Now (TS-correct)
position code ARP; >= 0 → N/E code POS; strictly > 0 → N/E (TS: 0 → S/W)
timestamp timestamp/TS/"Timestamp", raw int value time/TIMESTAMP/"Message Timestamp", timestampToString
callsign code CS code CALLSIGN
flight_number code FLT code FLIGHT; no-ops on empty string
tail label "Tail Number" label "Tail"
departure_airport airport_origin/DEP icao/ORG
arrival_airport airport_destination/ARR icao/DST
heading ° suffix plain number (TS)
fuel fuel/FUEL alias of current_fuel (fuel_on_board/FOB)

New methods (24)

  • Time-of-day (timestampToString display): eta, out, off, on, r#in
  • Calendar: day (MSG_DAY), month (MSG_MON), departure_day, arrival_day
  • Velocity/atmosphere: mach ("{} mach"), groundspeed (GSPD), airspeed (ASPD/"True Airspeed"), temperature(&str) with M→-/P→+ conversion, total_air_temp(&str)
  • Fuel: current_fuel (FOB), remaining_fuel (' FUEL_REM' — leading space preserved from TS for byte parity)
  • Routing: alternate_airport (ALT_DST), arrival_runway (ARWY), alternate_runway (ALT_ARWY)
  • Events: state_change, door_event
  • Text: text, unknown_sep, unknown_arr_sep
  • Diagnostics: checksum (TS 0x + tail-4 hex format), checksum_algorithm (raw only — the TS item push is commented out; mirrored exactly)
  • Generic: push_item, pub timestamp_to_string

timestamp_to_string implements TS DateTimeUtils.timestampToString's three-tier logic (HH:MM:SS / "YYYY-MM-DDTHH:MM:SS" masked / full ISO) via a chrono-free civil-from-days conversion — no new dependencies.

Known divergence (documented inline)

state_change emits raw from/to codes; TS routes them through RouteUtils.formatFlightState, which has no Rust port yet. Corpus reconciliation will flag affected samples.

Follow-up required on PR #4

The C branch (feat/c-runtime-formatters) carries the pre-verification code values (ARP, MSG_MONTH, ALT_RWY, GS, IAS, CKSUM, missing ' FUEL_REM' space, item-pushing checksum_algorithm). Those need the same corrections before #4 merges — will push fixes to that branch.

What this unblocks

  • The 12 remaining Rust todo!() hatch stubs lose their biggest blocker (missing formatter methods)
  • acars-decoder-rust's _shared.rs shim (~25 duplicated helpers) can be deleted in favor of the runtime once the submodule bumps
  • Cross-language corpus parity for every formatter-emitted item

Test plan

  • cargo build clean in runtimes/rust
  • CI on this PR
  • Follow-up: bump submodule in acars-decoder-rust, delete _shared.rs, re-point hatches at ResultFormatter

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Corrected aircraft position hemisphere handling for improved accuracy
    • Removed degree symbol from heading display
  • New Features

    • Enhanced timestamp formatting with time-of-day and ISO-8601 support
    • Expanded flight event logging including state changes and door events
    • Improved diagnostic information output with configurable separators

…ods)

Mirrors the C runtime expansion (PR #4) into ads_runtime::ResultFormatter,
with every type/code/label/value string cross-checked against the
production TS reference (runtimes/typescript/utils/result_formatter.ts)
this time — which surfaced several wrong codes in both the original
Rust formatters AND the C PR:

Corrections to EXISTING methods (TS-verified):
  - position:          code ARP → POS; direction uses strictly > 0
                       (TS: 0 → S/W)
  - timestamp:         kind/code/label "timestamp"/TS/"Timestamp" →
                       "time"/TIMESTAMP/"Message Timestamp"; value now
                       timestampToString (HH:MM:SS / ISO) not raw int
  - callsign:          code CS → CALLSIGN
  - flight_number:     code FLT → FLIGHT; no-ops on empty string (TS
                       guard `if (fields[3])`)
  - tail:              label "Tail Number" → "Tail"
  - departure_airport: kind/code "airport_origin"/DEP → "icao"/ORG
  - arrival_airport:   kind/code "airport_destination"/ARR → "icao"/DST
  - heading:           value drops the ° suffix (TS emits plain number)
  - fuel:              now an alias of current_fuel (fuel_on_board/FOB/
                       "Fuel On Board") — TS has no plain `fuel`

New methods:
  Time-of-day family (timestampToString display):
    eta, out, off, on, r#in
  Calendar: day (MSG_DAY), month (MSG_MON), departure_day, arrival_day
  Velocity/atmosphere: mach ("{} mach"), groundspeed (GSPD),
    airspeed (ASPD/"True Airspeed"), temperature(&str, M→-/P→+),
    total_air_temp(&str)
  Fuel: current_fuel (FOB), remaining_fuel (' FUEL_REM' — leading space
    preserved from TS for byte parity)
  Routing: alternate_airport (ALT_DST), arrival_runway (ARWY),
    alternate_runway (ALT_ARWY)
  Events: state_change, door_event
  Text: text, unknown_sep, unknown_arr_sep
  Diagnostics: checksum (0x%04x tail-4 format), checksum_algorithm
    (raw only — the TS item push is commented out; mirrored exactly)
  Generic: push_item + pub timestamp_to_string

timestamp_to_string implements TS DateTimeUtils.timestampToString's
three-tier logic (HH:MM:SS / "YYYY-MM-DDTHH:MM:SS" masked / full ISO)
via a chrono-free civil-from-days conversion.

Known divergence (documented inline): state_change emits raw from/to
codes; TS routes them through RouteUtils.formatFlightState which has
no Rust port yet. Corpus reconciliation will flag affected samples.

NOTE for PR #4 (C formatters): the C branch has the pre-verification
code values (ARP, MSG_MONTH, ALT_RWY, GS, IAS, CKSUM…). Will fix on
that branch to match before it merges.

Verified: cargo build clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The Rust ResultFormatter now mirrors the TypeScript static-class API with 20+ new public methods. A free timestamp_to_string function formats times using TS rules (time-of-day or ISO-8601). Measurement, temporal, identifier, fuel, and event emitters are added, each calling a public push_item wrapper. Position uses strict hemisphere logic; heading drops the degree symbol. Unknown-text accumulation now supports custom separators.

Changes

ResultFormatter API Expansion

Layer / File(s) Summary
Foundational: timestamp formatter and public push_item API
runtimes/rust/src/result_formatter.rs
Documentation updated to reflect TypeScript API mirroring. Free function timestamp_to_string(time: i64) -> String implements TS-style time-of-day and ISO-8601 formatting via civil-days conversion without external dependencies. Public method push_item(result, kind, code, label, value) wraps the internal push method to emit structured formatted items.
Measurement formatters: position, heading, and numerics
runtimes/rust/src/result_formatter.rs
Position hemisphere direction now uses strict > 0.0 / <= 0.0 comparisons and emits POS items instead of prior ARP code. Heading formatted output drops the degree symbol. New numeric emitters mach, groundspeed, airspeed and temperature parsers temperature, total_air_temp (accepting signed M/P prefixes) each emit both raw JSON fields and formatted items.
Time-of-day family and calendar formatters
runtimes/rust/src/result_formatter.rs
Time-of-day methods timestamp, eta, out, off, on, r#in use a shared push_tod helper that leverages timestamp_to_string. Calendar methods day, month, departure_day, arrival_day use a shared push_int helper for consistent integer emission.
Flight identifiers and location/fuel fields
runtimes/rust/src/result_formatter.rs
Callsign, flight number, and tail methods extract string values from JSON and push formatted items with updated codes and labels. New location emitters alternate_airport, arrival_runway, alternate_runway are introduced. Fuel helpers current_fuel, fuel (alias), and remaining_fuel (with leading-space code preservation) are added.
Event and diagnostic emitters
runtimes/rust/src/result_formatter.rs
New event emitters state_change(from, to), door_event(door, state), text(value), checksum (with TS-style hex formatting and 0x prefix), and checksum_algorithm(value) (raw JSON only, no formatted item) provide richer diagnostic output.
Separator-aware unknown text accumulation
runtimes/rust/src/result_formatter.rs
Original unknown and unknown_arr entry points now delegate to new helpers unknown_sep(value, sep) and unknown_arr_sep(values, sep), enabling custom join separators instead of hardcoded comma concatenation.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 A formatter blooms with methods grand and new,
Time strings dance in Rust, just like the TS crew,
Twenty voices speak of mach and fuel and doors,
Position flows north–south through logic's core,
API parity at last—hop towards the shore! 🎉

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 24.44% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically summarizes the main change: expanding the Rust ResultFormatter with 24 new methods to achieve full TypeScript parity.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/rust-runtime-formatters

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

kevinelliott added a commit that referenced this pull request Jun 10, 2026
Cross-checking against the production TS reference
(runtimes/typescript/utils/result_formatter.ts) while writing the Rust
mirror (PR #5) surfaced several wrong type/code/label strings in this
branch's original commit. Corrected to exact TS values:

Existing formatters:
  - position:          code ARP → POS; direction strictly > 0 → N/E
                       (TS: 0 → S/W)
  - timestamp:         timestamp/TS/"Timestamp" + numeric →
                       time/TIMESTAMP/"Message Timestamp" +
                       timestampToString display (moved after push_tod)
  - callsign:          CS → CALLSIGN
  - flight_number:     FLT → FLIGHT; no-ops on empty (TS guard)
  - tail:              "Tail Number" → "Tail"
  - departure_airport: airport_origin/DEP → icao/ORG
  - arrival_airport:   airport_destination/ARR → icao/DST
  - heading:           drops "deg" unit (TS emits plain number)
  - fuel:              fuel/FUEL/"Fuel" → fuel_on_board/FOB/
                       "Fuel On Board" (alias of currentFuel; TS has
                       no plain fuel)

New formatters from the earlier commit:
  - month:             MSG_MONTH/"Month" → MSG_MON/"Month of Year"
  - mach:              "Mach" → "Mach Number" + "{} mach" unit
  - groundspeed:       groundspeed/GS/"Ground Speed" →
                       aircraft_groundspeed/GSPD/"Aircraft Groundspeed"
  - airspeed:          IAS/"Indicated Airspeed" → ASPD/"True Airspeed"
  - temperature/total_air_temp: signature now (r, const char *) with
                       M→- / P→+ conversion + empty no-op (TS takes a
                       string); total_air_temp item type is
                       'temperature' (TS quirk)
  - remaining_fuel:    " FUEL_REM" leading space preserved (TS quirk)
  - alternate_runway:  ALT_RWY → ALT_ARWY
  - in/out labels:     "Arrival at Gate"/"Departure from Gate" →
                       "In Gate Time"/"Out of Gate Time"
  - checksum:          checksum/CKSUM/"Checksum"/%llx →
                       message_checksum/CHECKSUM/"Message Checksum"/
                       0x + zero-padded last-4 lowercase hex
  - checksum_algorithm: item push REMOVED — TS stores the raw field
                       only (its item block is commented out)

ads_fmt_time_of_day_str now implements TS timestampToString's full
three-tier logic (HH:MM:SS / "YYYY-MM-DD'T'HH:MM:SS" masked / full ISO)
via chrono-free civil-from-days.

Matches the Rust runtime expansion in PR #5 exactly; both runtimes now
emit byte-identical items per the TS reference.

Verified: runtimes/c builds clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2c1bf5e5e0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Self::push(result, "icao", "ALT_DST", "Alternate Destination", s);
}

pub fn arrival_runway(result: &mut DecodeResult, value: impl Into<Option<JsonValue>>) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Add the missing departure runway formatter

This runway formatter block adds arrival_runway and alternate_runway, but still omits the TS departureRunway counterpart (runway/DEPRWY/Departure Runway) even though the corpus contains DEPRWY/departure_runway outputs in arinc_702 samples. Any Rust hatch or future codegen path that needs parity for departure runway must either duplicate raw insertion plus item pushing or cannot call the parity API, so add departure_runway alongside these methods.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
runtimes/rust/src/result_formatter.rs (1)

374-382: 💤 Low value

Consider accepting a slice instead of Vec<String> by value.

Taking Vec<String> by value consumes the vector, forcing callers to give up ownership. Accepting &[String] or &[impl AsRef<str>] would allow callers to retain ownership and avoid unnecessary allocation when they already have a slice or borrowed data.

Suggested signature change
-    pub fn unknown_arr_sep(result: &mut DecodeResult, values: Vec<String>, sep: &str) {
+    pub fn unknown_arr_sep(result: &mut DecodeResult, values: &[String], sep: &str) {
         if values.is_empty() { return; }
         let joined = values.join(sep);
         Self::unknown_sep(result, &joined, sep);
     }

Then update unknown_arr to pass a reference:

     pub fn unknown_arr(result: &mut DecodeResult, values: Vec<String>) {
-        Self::unknown_arr_sep(result, values, ",");
+        Self::unknown_arr_sep(result, &values, ",");
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@runtimes/rust/src/result_formatter.rs` around lines 374 - 382, Change
unknown_arr and unknown_arr_sep to accept a slice (e.g., &[String] or, better,
&[impl AsRef<str>]) instead of consuming Vec<String> so callers don't lose
ownership; update the signatures of unknown_arr(result: &mut DecodeResult,
values: &[String]) and unknown_arr_sep(result: &mut DecodeResult, values:
&[String], sep: &str) (or use a generic AsRef-based slice) and change the
implementation to return early on values.is_empty(), join via values.join(sep)
or map AsRef::as_ref, and call Self::unknown_sep(result, &joined, sep); then
update all call sites that currently pass a Vec<String> to pass a slice
(.as_slice() or &vec) so ownership is preserved.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@runtimes/rust/src/result_formatter.rs`:
- Around line 374-382: Change unknown_arr and unknown_arr_sep to accept a slice
(e.g., &[String] or, better, &[impl AsRef<str>]) instead of consuming
Vec<String> so callers don't lose ownership; update the signatures of
unknown_arr(result: &mut DecodeResult, values: &[String]) and
unknown_arr_sep(result: &mut DecodeResult, values: &[String], sep: &str) (or use
a generic AsRef-based slice) and change the implementation to return early on
values.is_empty(), join via values.join(sep) or map AsRef::as_ref, and call
Self::unknown_sep(result, &joined, sep); then update all call sites that
currently pass a Vec<String> to pass a slice (.as_slice() or &vec) so ownership
is preserved.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 03f5e1c3-dfc2-4b0c-b83f-bf57556e439e

📥 Commits

Reviewing files that changed from the base of the PR and between e7c66e1 and 2c1bf5e.

📒 Files selected for processing (1)
  • runtimes/rust/src/result_formatter.rs

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.

1 participant