7.1 KiB
id, name, status, depends_on, scope, risk, impact, level
| id | name | status | depends_on | scope | risk | impact | level | |||
|---|---|---|---|---|---|---|---|---|---|---|
| typedef/tunion | Implement TUnion discriminator dispatch for byte-offset and field-name discriminators | completed |
|
narrow | medium | component | implementation |
Description
Implement TUnion discriminator dispatch in crates/alknet-typedef/src/tunion.rs.
TUnion supports two discriminator kinds (ADR-097 §4): byte-offset (protocol dispatch,
e.g., SFTP type bytes) and field-name (typedef.ts string pattern).
Per data-access.md §"TUnion Dispatch" and schema-layer.md §"TUnion discriminators".
Target shape
/// The result of reading a TUnion discriminator.
#[derive(Debug, Clone)]
pub struct UnionDispatch {
/// The mapping key (stringified discriminator value).
pub key: String,
/// The byte offset where the variant struct starts.
pub variant_offset: usize,
/// The size of the discriminator in bytes.
pub discriminator_size: usize,
}
/// Read the discriminator value from a byte-offset TUnion.
/// The discriminator is a fixed-size integer at a known byte offset.
/// Returns the mapping key (as a string) and the variant struct offset.
///
/// This is the SFTP `Packet` enum pattern — byte 0 is the type byte,
/// bytes 1..N are the variant struct. The call protocol's event type
/// dispatch uses the same pattern.
pub fn read_byte_discriminator(
buffer: &[u8],
union_schema: &serde_json::Value,
endian: Endian,
) -> Result<UnionDispatch, TypedefError>;
/// Read the discriminator value from a field-name TUnion.
/// The discriminator is a named field within the struct — its offset
/// is computed like any other field. The consumer provides the
/// discriminator field's offset (from the OffsetMap or LayoutBuilder).
///
/// This is the typedef.ts `TUnion` pattern — the discriminator is a
/// field like any other, and the mapping keys are string values.
pub fn read_field_discriminator(
buffer: &[u8],
union_schema: &serde_json::Value,
disc_field_offset: usize,
endian: Endian,
) -> Result<UnionDispatch, TypedefError>;
/// Look up a variant schema from the union's mapping.
/// Returns the variant schema (resolving `$ref` if needed).
pub fn resolve_variant<'a>(
union_schema: &'a serde_json::Value,
key: &str,
) -> Result<&'a serde_json::Value, TypedefError>;
/// Get the discriminator size in bytes for a byte-offset discriminator.
pub fn discriminator_size(union_schema: &serde_json::Value) -> Result<usize, TypedefError>;
Byte-offset discriminator
The discriminator is a fixed-size integer at a known byte offset. The mapping keys
are stringified integers ("5", "6", "101").
Supported discriminator types: TypeDef:Uint8 (1 byte), TypeDef:Uint16 (2 bytes),
TypeDef:Uint32 (4 bytes). The discriminator value is read using the appropriate
endian-aware read function from data_access.
The variant struct starts at offset + discriminator_size.
Field-name discriminator
The discriminator is a named field within the struct. Its offset is computed like any
other field (by the OffsetMap or LayoutBuilder). The mapping keys are string
values matching the discriminator field's value.
The discriminator field's TypeDef:* kind determines how to read it:
TypeDef:String→ read a length-prefixed stringTypeDef:Uint8→ read a u8, stringifyTypeDef:Enum→ read a u32 index, map to string value from"enum"array
Variant resolution
resolve_variant() looks up the mapping key in the union's "mapping" object.
Mapping values may be inline schemas or $ref pointers. $ref pointers are resolved
against the schema's $defs (the $ref normalization in schema.rs ensures they
are full JSON Pointer paths).
What this does NOT include
- The offset computation for TUnion (that's in
offset_map.rsandlayout_builder.rs) - The
TypedefEnginestruct (that'sengine.rs)
Acceptance Criteria
read_byte_discriminator()reads discriminator at known byte offset- Supports
TypeDef:Uint8,TypeDef:Uint16,TypeDef:Uint32discriminator types - Discriminator value read with correct endianness
- Returns mapping key as string (e.g.,
"5"for SFTP Read packet) - Returns correct
variant_offset(offset + discriminator_size) read_field_discriminator()reads discriminator from named field at given offset- Supports
TypeDef:String,TypeDef:Uint8,TypeDef:Enumdiscriminator field types resolve_variant()looks up mapping key and returns variant schemaresolve_variant()resolves$refpointers to$defsresolve_variant()returnsTypedefError::Schemafor unknown mapping keysdiscriminator_size()returns correct size for each discriminator type- Returns
TypedefError::Accessfor buffer-too-short - Returns
TypedefError::Schemafor malformed discriminator annotations - No
unwrap()orexpect()on error paths - All public functions have doc comments
cargo check -p alknet-typedefsucceedscargo clippy -p alknet-typedefsucceeds with no warningscargo build --workspacestill succeeds
References
- docs/architecture/crates/typedef/data-access.md — TUnion dispatch section
- docs/architecture/crates/typedef/schema-layer.md — TUnion discriminators section
- docs/architecture/decisions/097-schema-annotations.md — ADR-097 §4 (TUnion discriminators)
- /workspace/alknet-typedef-poc/src/lib.rs — POC reference (parse_union_discriminator, read_union_discriminator)
Notes
This is a focused module for TUnion discriminator dispatch. The two discriminator kinds cover both protocol dispatch (SFTP type bytes, call protocol event types) and the typedef.ts string pattern. The mapping keys are always strings — integer discriminator values are stringified. The POC 2's
parse_union_discriminatorandread_union_discriminatorfunctions are good references.
Summary
Implemented TUnion discriminator dispatch in crates/alknet-typedef/src/tunion.rs
with the UnionDispatch result type and four public functions: read_byte_discriminator,
read_field_discriminator, resolve_variant, and discriminator_size. Byte-offset
discriminators support TypeDef:Uint8/Uint16/Uint32 via the endian-aware
data_access readers, stringifying the integer value as the mapping key and returning
offset + discriminator_size as the variant start. Field-name discriminators support
TypeDef:String/Uint8/Enum fields, reading the value at the caller-provided offset
and computing the variant start as disc_field_offset + discriminator_field_size
(4 + str.len() for strings, 1 for u8, 4 for enum). resolve_variant returns inline
schemas directly and resolves "#/$defs/<name>" refs against the union schema's own
$defs block, returning TypedefError::Schema for unresolvable refs (the engine
layer is expected to inline or pass the root for nested unions). All 28 unit tests
pass, covering both discriminator kinds, all supported sub-types, both endiannesses,
buffer-too-short, unknown mapping keys, and ref resolution.