Skip to content

Commit 7ae1094

Browse files
userFRMclaude
andcommitted
feat(tdbe): ParsedRight::from_wire_byte (inverse of as_wire_byte)
Add a `const fn` inverse of the existing `ParsedRight::as_wire_byte`, decoding the FPSS wire byte (`67` for `'C'`, `80` for `'P'`) into a typed `ParsedRight`. Returns `None` for any other byte so the caller can lift the soft-skip / hard-error decision into its own error type. Round-trip property test confirms every variant whose `as_wire_byte()` returns `Some(b)` decodes through `from_wire_byte(b) == Some(self)`. The `Both` variant has no FPSS wire encoding, so the test asserts that path stays unrepresentable on the wire. Removes the rationale for downstream tick decoders (analytics chain snapshots, replay validators) to re-type the `67` / `80` magic numbers at every trust boundary. Patch bump tdbe 0.12.8 -> 0.12.9 per the 0.12.x patch-only policy; `thetadatadx` and `ffi` dependency pins lifted to match. No `thetadatadx` version bump in this commit -- a follow-up release PR will roll the SDK forward through the next patch. Closes #495. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 8e2bc3e commit 7ae1094

6 files changed

Lines changed: 100 additions & 5 deletions

File tree

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,17 @@ PR #489 (dispatcher core), #490 (C ABI), #492 (Python), #493
124124
shipped in this release. The `fpss_smoke` example is restored on
125125
the callback path.
126126

127+
### tdbe
128+
129+
- `tdbe::right::ParsedRight::from_wire_byte(byte: i32) -> Option<Self>`
130+
`const fn` decoder for the FPSS wire `right` byte (`67` for
131+
`'C'`, `80` for `'P'`). Inverse of the existing `as_wire_byte()`.
132+
Removes the rationale for downstream tick decoders to re-type the
133+
`67` / `80` magic numbers at every trust boundary; round-trip
134+
property test confirms `from_wire_byte(self.as_wire_byte().unwrap())
135+
== Some(self)` for every variant where the forward direction is
136+
defined. Patch bump tdbe 0.12.8 → 0.12.9.
137+
127138
## [8.0.29] - 2026-05-06
128139

129140
### Removed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/tdbe/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "tdbe"
3-
version = "0.12.8"
3+
version = "0.12.9"
44
edition.workspace = true
55
rust-version.workspace = true
66
authors.workspace = true

crates/tdbe/src/right.rs

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,39 @@ impl ParsedRight {
9797
Self::Both => None,
9898
}
9999
}
100+
101+
/// Decode an FPSS wire byte (`67` for `'C'`, `80` for `'P'`) into
102+
/// a typed [`ParsedRight`]. Returns [`None`] for any other byte
103+
/// so callers can lift the soft-skip / hard-error decision into
104+
/// their own error type.
105+
///
106+
/// Inverse of [`Self::as_wire_byte`]: every variant whose
107+
/// `as_wire_byte()` returns `Some(b)` round-trips through
108+
/// `from_wire_byte(b) == Some(self)`.
109+
///
110+
/// `const fn` so it stays evaluable in const contexts. Removes the
111+
/// rationale for downstream tick decoders (analytics chain
112+
/// snapshots, replay validators) to re-type the `67` / `80` magic
113+
/// numbers at every trust boundary.
114+
///
115+
/// # Examples
116+
///
117+
/// ```
118+
/// use tdbe::right::ParsedRight;
119+
///
120+
/// assert_eq!(ParsedRight::from_wire_byte(67), Some(ParsedRight::Call));
121+
/// assert_eq!(ParsedRight::from_wire_byte(80), Some(ParsedRight::Put));
122+
/// assert_eq!(ParsedRight::from_wire_byte(0), None);
123+
/// assert_eq!(ParsedRight::from_wire_byte(-1), None);
124+
/// ```
125+
#[must_use]
126+
pub const fn from_wire_byte(byte: i32) -> Option<Self> {
127+
match byte {
128+
67 => Some(Self::Call),
129+
80 => Some(Self::Put),
130+
_ => None,
131+
}
132+
}
100133
}
101134

102135
/// Parse a user-supplied `right` string.
@@ -247,4 +280,55 @@ mod tests {
247280
let err = parse_right_strict("xyz").unwrap_err();
248281
assert!(format!("{err}").contains("invalid option right"));
249282
}
283+
284+
#[test]
285+
fn from_wire_byte_decodes_call_and_put() {
286+
assert_eq!(ParsedRight::from_wire_byte(67), Some(ParsedRight::Call));
287+
assert_eq!(ParsedRight::from_wire_byte(80), Some(ParsedRight::Put));
288+
}
289+
290+
#[test]
291+
fn from_wire_byte_rejects_unknown_bytes() {
292+
for byte in [
293+
0_i32,
294+
1,
295+
65,
296+
66,
297+
68,
298+
79,
299+
81,
300+
100,
301+
256,
302+
-1,
303+
i32::MIN,
304+
i32::MAX,
305+
] {
306+
assert!(
307+
ParsedRight::from_wire_byte(byte).is_none(),
308+
"byte {byte} should not decode"
309+
);
310+
}
311+
}
312+
313+
#[test]
314+
fn wire_byte_round_trips_through_inverse() {
315+
// Every variant whose `as_wire_byte()` returns Some(b) should
316+
// round-trip through `from_wire_byte(b) == Some(self)`.
317+
for variant in [ParsedRight::Call, ParsedRight::Put, ParsedRight::Both] {
318+
match variant.as_wire_byte() {
319+
Some(byte) => {
320+
assert_eq!(
321+
ParsedRight::from_wire_byte(byte),
322+
Some(variant),
323+
"round-trip failed for {variant:?}"
324+
);
325+
}
326+
None => {
327+
// `Both` returns None on the forward direction —
328+
// there is no FPSS byte to invert from.
329+
assert_eq!(variant, ParsedRight::Both);
330+
}
331+
}
332+
}
333+
}
250334
}

crates/thetadatadx/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ frames = ["polars", "arrow"]
4040
live-tests = []
4141

4242
[dependencies]
43-
tdbe = { version = "0.12.8", path = "../tdbe" }
43+
tdbe = { version = "0.12.9", path = "../tdbe" }
4444

4545
# gRPC + protobuf (tonic 0.14 extracted prost codec into tonic-prost)
4646
tonic = { version = "=0.14.5", features = ["tls-ring", "tls-native-roots", "channel", "transport"] }
@@ -141,7 +141,7 @@ prost-build = "=0.14.3"
141141
regex = "1.12.3"
142142
toml = "1.1.2"
143143
serde = { version = "1.0.228", features = ["derive"] }
144-
tdbe = { version = "0.12.8", path = "../tdbe" }
144+
tdbe = { version = "0.12.9", path = "../tdbe" }
145145

146146
[[bench]]
147147
name = "bench_decode"

ffi/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ testing-panic-boundary = []
3131

3232
[dependencies]
3333
thetadatadx = { path = "../crates/thetadatadx" }
34-
tdbe = { version = "0.12.8", path = "../crates/tdbe" }
34+
tdbe = { version = "0.12.9", path = "../crates/tdbe" }
3535
tokio = { version = "1.52.1", features = ["rt-multi-thread"] }
3636
# Used by the FPSS streaming callback silent-drop observability path
3737
# (see `tdx_fpss_dropped_events` / `tdx_unified_dropped_events`). Keep

0 commit comments

Comments
 (0)