From 5e268e8f4769cbe0272ce7af2d34e82901ec5af2 Mon Sep 17 00:00:00 2001 From: "glm-5.2" Date: Sun, 2 Aug 2026 07:05:53 +0000 Subject: [PATCH] Rebrand TypeDef to AlkType in code, keyword strings, and docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename all 19 JSON Schema custom keyword strings from "TypeDef:*" to "AlkType:*" (e.g., "TypeDef:Struct" -> "AlkType:Struct") across source, tests, and docs. This is a breaking change to the schema format itself — existing schemas using the old keywords must be updated. Rename the Rust identifiers: - TypedefEngine -> AlkTypeEngine - TypedefError -> AlkTypeError - TypeDefKind -> AlkTypeKind - TYPEDEF_PREFIX -> ALKTYPE_PREFIX - get_typedef_kind{,_loose,_loose_enum,_enum} -> get_alktype_kind{,_loose,_loose_enum,_enum} Update error message strings ("unknown TypeDef kind" -> "unknown AlkType kind"), 11 test function names containing typedef_kind/to_typedef_error, and doc-comment prose ("TypeDef kind" -> "AlkType kind", "typedef engine" -> "alktype engine", "typedef schema" -> "alktype schema"). Fix the broken docs/architecture/crates/typedef/ path references in source doc comments to point at docs/architecture/ directly. Rebrand the typedef:annotation test fixture and the "not-a-typedef" test string to their alktype equivalents. Update ~20 generic "typedef" prose references in the architecture docs ("typedef is the binary struct engine", "use typedef", "typedef limitation", "replaced by typedef", etc.) to alktype. Rename TypedefEngine in the ADR-007 code example to AlkTypeEngine. Preserve as provenance per the prior prose-rebrand decision: typedef.ts references (external TypeBox source file), docs/research/alknet-typedef/findings.md research citations, /workspace/alknet-typedef-poc/ POC path, and the "alknet-typedef:" research section headers in findings. Build, 295 tests, and clippy all pass clean. --- Cargo.toml | 2 +- docs/architecture/README.md | 16 +- docs/architecture/data-access.md | 66 +- ...alktype-purpose-scope-jsonschema-engine.md | 34 +- .../002-two-layout-modes-packed-vs-aligned.md | 2 +- .../decisions/003-schema-annotations.md | 46 +- .../004-error-handling-validation-strategy.md | 48 +- .../005-int64-uint64-first-class-kinds.md | 36 +- ...-inline-length-prefixed-in-aligned-mode.md | 8 +- .../decisions/007-packed-mode-read-factory.md | 4 +- .../008-reject-tunion-in-aligned-mode.md | 6 +- docs/architecture/layout-engine.md | 30 +- docs/architecture/overview.md | 40 +- ...003-builder-api-for-schema-construction.md | 2 +- docs/architecture/schema-layer.md | 150 ++--- docs/architecture/validation.md | 96 +-- src/data_access.rs | 66 +- src/engine.rs | 274 ++++---- src/error.rs | 24 +- src/layout_builder.rs | 588 +++++++++--------- src/lib.rs | 16 +- src/macros.rs | 10 +- src/offset_map.rs | 254 ++++---- src/schema.rs | 444 ++++++------- src/sequential_reader.rs | 408 ++++++------ src/tunion.rs | 212 +++---- src/validation.rs | 178 +++--- tests/engine_integration.rs | 208 +++---- tests/error_paths.rs | 164 ++--- tests/poc_roundtrip.rs | 144 ++--- tests/tunion_dispatch.rs | 126 ++-- 31 files changed, 1851 insertions(+), 1851 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 098bff1..519d598 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ name = "alktype" version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" -description = "Binary struct engine: takes a JSON Schema with TypeDef:* custom keywords and produces an offset map, read/write functions, and validation" +description = "Binary struct engine: takes a JSON Schema with AlkType:* custom keywords and produces an offset map, read/write functions, and validation" repository = "https://git.alk.dev/alkdev/alktype" [lib] diff --git a/docs/architecture/README.md b/docs/architecture/README.md index ab934ab..501f175 100644 --- a/docs/architecture/README.md +++ b/docs/architecture/README.md @@ -6,7 +6,7 @@ last_updated: 2026-07-22 # alktype The binary struct engine: a small Rust crate that takes a JSON Schema -with `TypeDef:*` custom keywords and produces an offset map, read/write +with `AlkType:*` custom keywords and produces an offset map, read/write functions, and validation — all driven by the schema. The schema is the format definition; the engine is generic. @@ -15,10 +15,10 @@ format definition; the engine is generic. | Document | Status | Description | |----------|--------|-------------| | [overview.md](overview.md) | draft | Crate purpose, "schema is the format" principle, dependencies, consumers, scope boundaries | -| [schema-layer.md](schema-layer.md) | draft | The 19 `TypeDef:*` kinds, jsonschema custom keyword integration, TypeBox interop, schema annotations | +| [schema-layer.md](schema-layer.md) | draft | The 19 `AlkType:*` kinds, jsonschema custom keyword integration, TypeBox interop, schema annotations | | [layout-engine.md](layout-engine.md) | draft | Offset computation, the two layout modes (packed sequential vs aligned static), alignment, endianness, variable-length handling | | [data-access.md](data-access.md) | draft | Read/write functions, TUnion dispatch, field paths, zero-copy access, length-prefix reading | -| [validation.md](validation.md) | draft | Custom keyword validators for all 19 `TypeDef:*` kinds, `TypedefError`, load-time vs access-time validation, `TypedefEngine` | +| [validation.md](validation.md) | draft | Custom keyword validators for all 19 `AlkType:*` kinds, `AlkTypeError`, load-time vs access-time validation, `AlkTypeEngine` | ## Applicable ADRs @@ -27,7 +27,7 @@ format definition; the engine is generic. | [001](decisions/001-alktype-purpose-scope-jsonschema-engine.md) | Purpose, Scope, and the jsonschema Engine | What the crate is/isn't; why jsonschema not a custom engine; "schema is the format" principle; scope boundaries | | [002](decisions/002-two-layout-modes-packed-vs-aligned.md) | Two Layout Modes — Packed Sequential vs Aligned Static | The most important architectural finding; when to use each mode; `LayoutBuilder`/`SequentialReader` vs `OffsetMap` | | [003](decisions/003-schema-annotations.md) | Schema Annotations — Endianness, Alignment, Encoding, TUnion Discriminators | Concrete JSON shapes for all schema-level annotations | -| [004](decisions/004-error-handling-validation-strategy.md) | Error Handling and Validation Strategy | `TypedefError` enum; load-time build, access-time check; field-path-carrying errors | +| [004](decisions/004-error-handling-validation-strategy.md) | Error Handling and Validation Strategy | `AlkTypeError` enum; load-time build, access-time check; field-path-carrying errors | | [005](decisions/005-int64-uint64-first-class-kinds.md) | Int64/Uint64 as First-Class Kinds | 64-bit integers (SFTP offsets, metatensor data_offsets); JSON precision caveat | | [006](decisions/006-reject-non-final-inline-length-prefixed-in-aligned-mode.md) | Reject Non-Final Inline Length-Prefixed Variable Fields in Aligned Mode | Prevents silent data corruption (inline variable data clobbering subsequent fields) | | [007](decisions/007-packed-mode-read-factory.md) | Packed-Mode Read API — Engine as SequentialReader Factory | `engine.sequential_reader()` returns an owned reader, not a reference | @@ -43,7 +43,7 @@ format definition; the engine is generic. ## Key Design Principles -1. **The schema is the format.** A JSON Schema with `TypeDef:*` custom +1. **The schema is the format.** A JSON Schema with `AlkType:*` custom keywords is both the validation spec and the layout spec. No separate format definition, no separate parser, no separate validator. One schema, three uses: validate, compute offsets, access data. See @@ -88,11 +88,11 @@ format definition; the engine is generic. [validation.md](validation.md) and [ADR-004](decisions/004-error-handling-validation-strategy.md). -8. **Not a serialization framework.** The typedef engine is not a +8. **Not a serialization framework.** The alktype engine is not a general-purpose serde replacement. It operates on raw byte buffers at computed offsets — no intermediate `Value` tree, no reflection, no dynamic dispatch per field. For JSON data, use serde. For binary data - with a known schema, use typedef. See [overview.md](overview.md) and + with a known schema, use alktype. See [overview.md](overview.md) and [ADR-001](decisions/001-alktype-purpose-scope-jsonschema-engine.md). ## References @@ -106,5 +106,5 @@ format definition; the engine is generic. schema kinds (619 lines) - `/workspace/jsonschema/` — the jsonschema crate (v0.46.5, Draft 2020-12) - `/workspace/alknet-typedef-poc/` — the POC code (disposable) -- `/workspace/@alkimiadev/typebox-rs/` — prior attempt, replaced by typedef +- `/workspace/@alkimiadev/typebox-rs/` — prior attempt, replaced by alktype - `/workspace/@alkimiadev/alktype/` — prior attempt (the @alkimiadev/alktype prototype, a handler-registry pattern; not to be confused with this crate, which reuses the name but is backed by the `jsonschema` crate) diff --git a/docs/architecture/data-access.md b/docs/architecture/data-access.md index be484c6..d68edd8 100644 --- a/docs/architecture/data-access.md +++ b/docs/architecture/data-access.md @@ -8,7 +8,7 @@ last_updated: 2026-07-22 The data access layer: read/write functions, TUnion dispatch, field paths, zero-copy access for fixed-size types, and length-prefix reading for variable-length types. This is the consumer-facing API — given a compiled -`TypedefEngine` and a byte buffer, read and write fields at +`AlkTypeEngine` and a byte buffer, read and write fields at schema-computed offsets. This document covers two layers: @@ -16,11 +16,11 @@ This document covers two layers: - **Primitive read/write functions** in the `data_access` module — typed reads/writes at a caller-provided offset. These are the building blocks used by the layout types (`OffsetMap`, `LayoutBuilder`, - `SequentialReader`) and the `TypedefEngine`. Each operates on a raw - byte buffer at a known offset and returns a `TypedefError::Access` + `SequentialReader`) and the `AlkTypeEngine`. Each operates on a raw + byte buffer at a known offset and returns a `AlkTypeError::Access` carrying the field path on bounds or encoding failures. - **The `FieldValue` enum and the higher-level APIs** — - `TypedefEngine::read_field`/`write_field` (aligned mode) and + `AlkTypeEngine::read_field`/`write_field` (aligned mode) and `SequentialReader::read_next`/`read_field` (packed mode) — which look up a field's offset via the layout and dispatch to the primitive functions, returning a unified `FieldValue<'a>`. @@ -55,7 +55,7 @@ types, signalling the consumer must walk each element sequentially. ## Read/Write Model -The typedef engine operates on raw byte buffers (`&[u8]` for reading, +The alktype engine operates on raw byte buffers (`&[u8]` for reading, `&mut [u8]` for writing). There is no intermediate `Value` tree, no reflection, no dynamic dispatch per field. The engine uses the offset map (or `LayoutBuilder`/`SequentialReader`) to locate fields, then performs @@ -63,21 +63,21 @@ typed access at the computed positions. ### Higher-level read/write -The `TypedefEngine` and `SequentialReader` provide the primary +The `AlkTypeEngine` and `SequentialReader` provide the primary consumer-facing read/write APIs. They look up a field's offset via the layout and dispatch to the primitive `data_access` functions, returning `FieldValue` (read) or accepting `&FieldValue` (write). ```rust -impl TypedefEngine { +impl AlkTypeEngine { // Aligned mode: looks up the field's ByteRange in the OffsetMap, - // dispatches to the right data_access function by TypeDefKind. - // Returns TypedefError::Access if compiled in packed mode + // dispatches to the right data_access function by AlkTypeKind. + // Returns AlkTypeError::Access if compiled in packed mode // (use sequential_reader() for packed mode). pub fn read_field<'a>(&self, buffer: &'a [u8], field_path: &str) - -> Result, TypedefError>; + -> Result, AlkTypeError>; pub fn write_field(&self, buffer: &mut [u8], field_path: &str, - value: &FieldValue<'_>) -> Result<(), TypedefError>; + value: &FieldValue<'_>) -> Result<(), AlkTypeError>; // Packed mode: returns an owned fresh SequentialReader (ADR-007). // Each call returns a new reader with the cursor at position 0. @@ -90,16 +90,16 @@ impl SequentialReader { // prefixes to find each field's position. read_field walks all // preceding fields to reach the target. pub fn read_next<'a>(&mut self, buffer: &'a [u8]) - -> Result)>, TypedefError>; + -> Result)>, AlkTypeError>; pub fn read_field<'a>(&mut self, buffer: &'a [u8], field_path: &str) - -> Result, TypedefError>; + -> Result, AlkTypeError>; pub fn reset(&mut self); pub fn position(&self) -> usize; pub fn endian(&self) -> Endian; } ``` -`read_field`/`write_field` on `TypedefEngine` work for the fixed-size +`read_field`/`write_field` on `AlkTypeEngine` work for the fixed-size primitive kinds and the length-prefixed `String`/`Bytes`/`Timestamp` fields. Composite kinds (`Struct`, `Union`, `Array`, `Record`) return a `FieldValue` carrying a layout descriptor (byte range, variant start, @@ -115,7 +115,7 @@ which the builder consumes at `build` time. The `data_access` module exposes typed read/write functions for each primitive kind. Each takes `field_path: &str` for error attribution -(produces a `TypedefError::Access` carrying the path on bounds or +(produces a `AlkTypeError::Access` carrying the path on bounds or encoding failures) and, for multi-byte types, an `Endian` parameter. ### Fixed-size types @@ -126,7 +126,7 @@ accessed via zero-copy reads of N bytes at the offset: ```rust // Read a u32 at a known offset, applying endianness. Bounds-checked. fn read_u32(buffer: &[u8], offset: usize, field_path: &str, endian: Endian) - -> Result { + -> Result { let bytes: [u8; 4] = read_array(buffer, offset, field_path)?; Ok(match endian { Endian::Little => u32::from_le_bytes(bytes), @@ -136,7 +136,7 @@ fn read_u32(buffer: &[u8], offset: usize, field_path: &str, endian: Endian) // Write a u32 at a known offset, applying endianness. Bounds-checked. fn write_u32(buffer: &mut [u8], offset: usize, value: u32, - field_path: &str, endian: Endian) -> Result<(), TypedefError> { + field_path: &str, endian: Endian) -> Result<(), AlkTypeError> { let bytes = match endian { Endian::Little => value.to_le_bytes(), Endian::Big => value.to_be_bytes(), @@ -148,7 +148,7 @@ fn write_u32(buffer: &mut [u8], offset: usize, value: u32, The engine applies endianness at access time based on the schema's `"endian"` annotation (ADR-003). The offset computation is endian-agnostic. The `read_array`/`write_array` helpers perform the -bounds check and produce `TypedefError::Access` with the field path on +bounds check and produce `AlkTypeError::Access` with the field path on failure. ### TEnum access @@ -158,7 +158,7 @@ to the `u32` primitives, applying the schema's endianness: ```rust pub fn read_enum(buffer: &[u8], offset: usize, field_path: &str, endian: Endian) - -> Result { + -> Result { read_u32(buffer, offset, field_path, endian) } ``` @@ -179,11 +179,11 @@ attribution and `endian` for the length prefix: ```rust // Read a length-prefixed string, borrowing from the buffer. fn read_string<'a>(buffer: &'a [u8], offset: usize, - field_path: &str, endian: Endian) -> Result<&'a str, TypedefError>; + field_path: &str, endian: Endian) -> Result<&'a str, AlkTypeError>; // Write a length-prefixed string. Returns total bytes written (4 + data.len()). fn write_string(buffer: &mut [u8], offset: usize, value: &str, - field_path: &str, endian: Endian) -> Result; + field_path: &str, endian: Endian) -> Result; // read_bytes / write_bytes have the same shape — raw bytes, no UTF-8 check. ``` @@ -210,10 +210,10 @@ bytes live in a separate `data_region`: ```rust fn read_string_indirect<'a>(buffer: &'a [u8], offset: usize, data_region: &'a [u8], field_path: &str, - endian: Endian) -> Result<&'a str, TypedefError>; + endian: Endian) -> Result<&'a str, AlkTypeError>; fn read_bytes_indirect<'a>(buffer: &'a [u8], offset: usize, data_region: &'a [u8], field_path: &str, - endian: Endian) -> Result<&'a [u8], TypedefError>; + endian: Endian) -> Result<&'a [u8], AlkTypeError>; ``` The field is a struct `{offset: u32, length: u32}` at a known position @@ -247,14 +247,14 @@ fresh `SequentialReader` scoped to the variant). ```rust /// Read the discriminator value from a byte-offset TUnion. The discriminator -/// is a fixed-size integer (TypeDef:Uint8/Uint16/Uint32) at a known byte +/// is a fixed-size integer (AlkType:Uint8/Uint16/Uint32) at a known byte /// offset. Returns the mapping key (stringified integer) and the variant /// struct offset. pub fn read_byte_discriminator( buffer: &[u8], union_schema: &Value, endian: Endian, -) -> Result; +) -> Result; ``` This is the SFTP `Packet` enum pattern — byte 0 is the type byte, bytes @@ -268,14 +268,14 @@ starts at `offset + discriminator_size`. /// Read the discriminator value from a field-name TUnion. The /// discriminator is a named field within the struct — the consumer /// provides the field's computed offset (from the OffsetMap or -/// LayoutBuilder). Supports TypeDef:String, Uint8, and Enum discriminator +/// LayoutBuilder). Supports AlkType:String, Uint8, and Enum discriminator /// fields. pub fn read_field_discriminator( buffer: &[u8], union_schema: &Value, disc_field_offset: usize, endian: Endian, -) -> Result; +) -> Result; ``` The discriminator is a named field within the struct. Its offset is @@ -291,12 +291,12 @@ the variant's fields starting at the end of the discriminator field. /// are returned directly. $ref pointers of the form "#/$defs/" /// are resolved against the union schema's own $defs block. pub fn resolve_variant<'a>(union_schema: &'a Value, key: &str) - -> Result<&'a Value, TypedefError>; + -> Result<&'a Value, AlkTypeError>; /// Get the discriminator's byte size (1/2/4 for Uint8/16/32) for a /// byte-offset TUnion. Field-name discriminators have no fixed size -/// and produce a TypedefError::Schema. -pub fn discriminator_size(union_schema: &Value) -> Result; +/// and produce a AlkTypeError::Schema. +pub fn discriminator_size(union_schema: &Value) -> Result; ``` ### TUnion in the layout engines @@ -318,13 +318,13 @@ variant before recursing. Fields are addressed by dotted paths: `"header.version"`, `"payload.data"`. Both `OffsetMap` and `PackedLayout` store fully-qualified paths (nested struct fields appear under their parent's path prefix). The higher-level -APIs (`TypedefEngine::read_field`/`write_field`, `SequentialReader::read_field`) +APIs (`AlkTypeEngine::read_field`/`write_field`, `SequentialReader::read_field`) accept a field path, look up the byte range/position in the layout, and dispatch to the primitive `data_access` function for the field's kind. -For aligned-mode access, `TypedefEngine::read_field(&buffer, "header.version")` +For aligned-mode access, `AlkTypeEngine::read_field(&buffer, "header.version")` returns `FieldValue` — it looks up the `ByteRange` in the `OffsetMap`, finds -the field's `TypeDef:*` kind in the schema, and calls the matching +the field's `AlkType:*` kind in the schema, and calls the matching `data_access::read_*` function. `write_field` is the mirror. Composite kinds (`Struct`, `Union`, `Array`, `Record`) return a `FieldValue` carrying a layout descriptor; the consumer recurses with a fresh reader diff --git a/docs/architecture/decisions/001-alktype-purpose-scope-jsonschema-engine.md b/docs/architecture/decisions/001-alktype-purpose-scope-jsonschema-engine.md index 688a07f..7d230d3 100644 --- a/docs/architecture/decisions/001-alktype-purpose-scope-jsonschema-engine.md +++ b/docs/architecture/decisions/001-alktype-purpose-scope-jsonschema-engine.md @@ -18,7 +18,7 @@ bytes at computed offsets. (`Read { id: u32, handle: String, offset: u64, len: u32 }`). The wire format is `[length: u32][type: u8][payload]` where payload is the struct's serde bytes. The `Packet` enum dispatches on the type byte — - a tagged union of structs. Under the typedef lens, each packet is a + a tagged union of structs. Under the alktype lens, each packet is a `TStruct`; the `Packet` enum is a `TUnion` with a byte-offset discriminator. @@ -27,7 +27,7 @@ bytes at computed offsets. compute byte offsets for each field so the consumer can read tensor data at known positions without parsing. -The common pattern: **a JSON Schema with `TypeDef:*` custom keywords +The common pattern: **a JSON Schema with `AlkType:*` custom keywords describes the shape of binary data; the binary data is the struct's bytes at computed offsets.** The schema is the format definition; the engine is generic. @@ -51,16 +51,16 @@ validation. The call-channels-unification research (`docs/research/call-channels-unification/findings.md` §"alknet-typedef: JSON Schema as the binary struct engine") identified the convergence and -bumped typedef up in the timeline. The POC +bumped alktype up in the timeline. The POC (`docs/research/alknet-typedef/findings.md`, 26 tests passing) validated the approach: a ~1,900-line Rust crate that takes a JSON Schema with -`TypeDef:*` custom keywords and produces an offset map, read/write +`AlkType:*` custom keywords and produces an offset map, read/write functions, and validation — all driven by the schema. ## Decision **alktype is a small Rust crate that takes a JSON Schema with -`TypeDef:*` custom keywords and produces three capabilities:** +`AlkType:*` custom keywords and produces three capabilities:** 1. **An offset map** — walks the schema, computes byte offsets for each field based on type sizes, field order, and alignment. @@ -79,8 +79,8 @@ functions, and validation — all driven by the schema. — a recursive walk of the schema JSON that computes byte positions for each field. The custom keyword implementations are ~10 lines each. -**The schema is the format.** A JSON Schema with `TypeDef:Float32`, -`TypeDef:Struct`, `TypeDef:Union` etc. is both the validation spec and +**The schema is the format.** A JSON Schema with `AlkType:Float32`, +`AlkType:Struct`, `AlkType:Union` etc. is both the validation spec and the layout spec. No separate format definition, no separate parser, no separate validator. One schema, three uses: validate, compute offsets, access data. @@ -88,8 +88,8 @@ access data. **The crate depends on `jsonschema` and `serde_json` (with `preserve_order`).** No tokio, no platform deps. Compiles to `wasm32-unknown-unknown` for browser use. The `jsonschema` crate's -`with_keyword("TypeDef:Float32", factory)` API is the integration point -for custom type kinds — each `TypeDef:*` kind maps to a custom keyword +`with_keyword("AlkType:Float32", factory)` API is the integration point +for custom type kinds — each `AlkType:*` kind maps to a custom keyword validator in Rust. Same semantics as TypeBox's `TypeRegistry.Set`, same JSON Schema wire format. @@ -113,7 +113,7 @@ be added as a feature gate later — the engine's core (offset computation, adding a variant to the schema JSON, not writing a new Rust struct + serde impl. The engine is generic; the schema is the configuration. - **WASM-clean.** `serde_json` + `jsonschema` + byte manipulation. No - tokio, no platform deps. The same typedef schemas work in browser, + tokio, no platform deps. The same alktype schemas work in browser, Node, Python (via `wasmtime-py`), Go (via `wazero`), and any other WASM host. - **TypeBox interop.** TypeBox modules render to standard JSON Schema @@ -135,27 +135,27 @@ be added as a feature gate later — the engine's core (offset computation, load-bearing for binary layouts. The `preserve_order` feature adds a small compile-time cost. - **Schema authoring is external.** Schemas are authored in TypeBox (JS) - or hand-written JSON. The typedef engine consumes schemas; it does not + or hand-written JSON. The alktype engine consumes schemas; it does not generate them. A builder API is deferred (OQ-003). ## Scope Boundaries (What This Is Not) -- **Not metatensor.** typedef is the binary struct *engine*. Metatensor +- **Not metatensor.** alktype is the binary struct *engine*. Metatensor is a *format* (8-byte header + JSON header + binary data) that uses the - typedef engine for its offset computation and tensor access. + alktype engine for its offset computation and tensor access. - **Not a Value system.** TypeBox's `Value.Diff`, `Value.Migrate`, `Value.Convert` — schema evolution — is out of scope for v1. The engine should not do anything that explicitly blocks adding a Value system later. - **Not a code generator.** typebox-rs's `codegen/` module is a separate - concern. The typedef engine consumes schemas; it does not generate them. -- **Not a schema builder.** The typedef engine does not provide a fluent + concern. The alktype engine consumes schemas; it does not generate them. +- **Not a schema builder.** The alktype engine does not provide a fluent API for constructing schemas. Schemas are plain JSON. -- **Not a serialization framework.** The typedef engine is not a +- **Not a serialization framework.** The alktype engine is not a general-purpose serde replacement. It operates on raw byte buffers at computed offsets — no intermediate `Value` tree, no reflection, no dynamic dispatch per field. For JSON data, use serde. For binary data - with a known schema, use typedef. + with a known schema, use alktype. ## References diff --git a/docs/architecture/decisions/002-two-layout-modes-packed-vs-aligned.md b/docs/architecture/decisions/002-two-layout-modes-packed-vs-aligned.md index 68fb82f..8935f88 100644 --- a/docs/architecture/decisions/002-two-layout-modes-packed-vs-aligned.md +++ b/docs/architecture/decisions/002-two-layout-modes-packed-vs-aligned.md @@ -41,7 +41,7 @@ tensor data). ## Decision -**The typedef engine supports two layout modes, selected by the consumer +**The alktype engine supports two layout modes, selected by the consumer at engine construction time:** ### Mode 1: Packed sequential (`LayoutBuilder` / `SequentialReader`) diff --git a/docs/architecture/decisions/003-schema-annotations.md b/docs/architecture/decisions/003-schema-annotations.md index a78f387..6d9db82 100644 --- a/docs/architecture/decisions/003-schema-annotations.md +++ b/docs/architecture/decisions/003-schema-annotations.md @@ -5,7 +5,7 @@ Accepted ## Context -The typedef engine needs concrete JSON shapes for schema-level +The alktype engine needs concrete JSON shapes for schema-level annotations that control binary layout behavior. The POCs validated the semantics; this ADR pins the shapes. @@ -28,7 +28,7 @@ Four annotation categories need concrete shapes: ```json { - "TypeDef:Struct": true, + "AlkType:Struct": true, "endian": "big", "properties": { ... } } @@ -49,11 +49,11 @@ struct-level.** ```json { - "TypeDef:Struct": true, + "AlkType:Struct": true, "align": 256, "properties": { - "header": { "TypeDef:Struct": true, "properties": { ... } }, - "weight": { "TypeDef:Float32": true, "align": 16 } + "header": { "AlkType:Struct": true, "properties": { ... } }, + "weight": { "AlkType:Float32": true, "align": 16 } } } ``` @@ -76,16 +76,16 @@ annotation and the standard JSON Schema `maxLength` keyword.** ```json // Strategy 1: Inline length-prefixing (default, shorthand) -{ "TypeDef:String": true } +{ "AlkType:String": true } // Strategy 1: Explicit inline length-prefixing -{ "TypeDef:String": { "encoding": "length-prefixed" } } +{ "AlkType:String": { "encoding": "length-prefixed" } } // Strategy 2: Fixed-size reservation (uses standard maxLength) -{ "TypeDef:String": true, "maxLength": 256 } +{ "AlkType:String": true, "maxLength": 256 } // Strategy 3: Offset indirection (opt-in) -{ "TypeDef:String": { "encoding": "offset-indirect" } } +{ "AlkType:String": { "encoding": "offset-indirect" } } ``` **Strategy 1: Inline length-prefixing (default).** The field's fixed @@ -126,27 +126,27 @@ reserving worst-case space. - `true` is a shorthand for the default (length-prefixed). This keeps the common case concise and the override explicit. - The `encoding` annotation and `maxLength` apply to all variable-length - types: `TypeDef:String`, `TypeDef:Bytes`, `TypeDef:Array`, - `TypeDef:Record`, `TypeDef:Timestamp`. + types: `AlkType:String`, `AlkType:Bytes`, `AlkType:Array`, + `AlkType:Record`, `AlkType:Timestamp`. ### 3a. TRecord value type -`TypeDef:Record` is a string-keyed map. The value type is declared via +`AlkType:Record` is a string-keyed map. The value type is declared via the `"values"` property in the schema: ```json { - "TypeDef:Record": true, - "values": { "TypeDef:Float32": true } + "AlkType:Record": true, + "values": { "AlkType:Float32": true } } ``` -- `"values"` is a schema object declaring the `TypeDef:*` kind of all +- `"values"` is a schema object declaring the `AlkType:*` kind of all values in the record. All values share the same type. - The binary layout is a count-prefixed sequence of `(key, value)` pairs: `[count: u32][key_len: u32][key_bytes][value]...` repeated `count` times. Each key is a length-prefixed UTF-8 string. Each value is - encoded according to its declared `TypeDef:*` kind — a `Record` + encoded according to its declared `AlkType:*` kind — a `Record` value is 4 raw bytes; a `Record` value is itself a length-prefixed string; a `Record` value is the struct's fields laid out inline. There is **no separate `value_len` prefix** — @@ -165,11 +165,11 @@ field-name (typedef.ts pattern).** ```json { - "TypeDef:Union": true, + "AlkType:Union": true, "discriminator": { "kind": "byte", "offset": 0, - "type": "TypeDef:Uint8" + "type": "AlkType:Uint8" }, "mapping": { "1": { "$ref": "#/$defs/Init" }, @@ -184,8 +184,8 @@ field-name (typedef.ts pattern).** - The discriminator is a fixed-size integer at a known byte offset. - `"offset"` is the byte position of the discriminator within the union's buffer. -- `"type"` is the `TypeDef:*` kind of the discriminator (typically - `TypeDef:Uint8` for protocol type bytes). +- `"type"` is the `AlkType:*` kind of the discriminator (typically + `AlkType:Uint8` for protocol type bytes). - The mapping keys are stringified integers (`"1"`, `"5"`, `"101"`). The engine parses the key to match the discriminator value. - The variant struct starts at `offset + discriminator_size`. @@ -196,7 +196,7 @@ field-name (typedef.ts pattern).** ```json { - "TypeDef:Union": true, + "AlkType:Union": true, "discriminator": { "kind": "field", "name": "type" @@ -240,8 +240,8 @@ event types). Both work. ### Negative -- **Keyword value shape change.** `"TypeDef:String": true` (boolean) and - `"TypeDef:String": { "encoding": "length-prefixed" }` (object) are both +- **Keyword value shape change.** `"AlkType:String": true` (boolean) and + `"AlkType:String": { "encoding": "length-prefixed" }` (object) are both valid. The engine must handle both shapes. This is a minor parsing concern — the POC already handles it. - **Alignment annotations are mode-specific.** Alignment is only diff --git a/docs/architecture/decisions/004-error-handling-validation-strategy.md b/docs/architecture/decisions/004-error-handling-validation-strategy.md index db7a723..c858336 100644 --- a/docs/architecture/decisions/004-error-handling-validation-strategy.md +++ b/docs/architecture/decisions/004-error-handling-validation-strategy.md @@ -5,11 +5,11 @@ Accepted ## Context -The typedef engine operates in three phases, each with distinct error +The alktype engine operates in three phases, each with distinct error conditions: 1. **Schema parsing** — invalid JSON, missing required keywords, unknown - `TypeDef:*` kinds, malformed annotations. + `AlkType:*` kinds, malformed annotations. 2. **Offset computation** — field not found, type not supported for offset computation, recursive schema depth exceeded. 3. **Read/write** — buffer too short, invalid UTF-8, value out of range @@ -23,12 +23,12 @@ time (validate each buffer). ## Decision -### Error type: `TypedefError` +### Error type: `AlkTypeError` -A single `TypedefError` enum with variants for each error category: +A single `AlkTypeError` enum with variants for each error category: ```rust -pub enum TypedefError { +pub enum AlkTypeError { /// Schema parsing errors. Schema(String), /// Offset computation errors. @@ -41,39 +41,39 @@ pub enum TypedefError { ``` - `Schema` — for invalid JSON, missing required keywords, unknown - `TypeDef:*` kinds. The error message describes the problem. + `AlkType:*` kinds. The error message describes the problem. - `Offset` — for field-not-found, unsupported type for offset computation, etc. Carries the field path for debugging. - `Access` — for buffer-too-short, invalid UTF-8, value out of range. Carries the field path for debugging. - `Validation` — wraps `jsonschema`'s `ValidationError`. The `jsonschema` crate already provides rich error messages with schema - paths; the typedef engine does not re-wrap or re-interpret them. + paths; the alktype engine does not re-wrap or re-interpret them. The `Validation` variant uses `ValidationError<'static>` because the validator is built once at schema load time and lives for the lifetime -of the `TypedefEngine`. The `'static` lifetime is correct — the validator +of the `AlkTypeEngine`. The `'static` lifetime is correct — the validator owns its schema reference. ### Validation timing: load-time build, access-time check The jsonschema validator is built once at schema load time (`validator_for(&schema)?`) and then called repeatedly -(`validator.is_valid(&instance)`). The typedef engine follows the same +(`validator.is_valid(&instance)`). The alktype engine follows the same pattern: 1. **Load time:** Parse the schema JSON, build the offset map (or `LayoutBuilder`/`SequentialReader`), build the jsonschema validator. - This is the `TypedefEngine::compile(schema: &Value) -> Result` constructor. + This is the `AlkTypeEngine::compile(schema: &Value) -> Result` constructor. 2. **Access time:** Use the compiled engine for repeated read/write operations. Validation is opt-in per operation — the consumer calls `engine.validate(buffer)` when validation is desired. -The `TypedefEngine` struct is the compiled form of a schema: +The `AlkTypeEngine` struct is the compiled form of a schema: ```rust -pub struct TypedefEngine { +pub struct AlkTypeEngine { offset_map: OffsetMap, // or LayoutBuilder/SequentialReader validator: jsonschema::Validator, // compiled once at load time } @@ -81,21 +81,21 @@ pub struct TypedefEngine { ### Custom keyword validators -Each `TypeDef:*` kind gets a `Keyword` implementation registered via +Each `AlkType:*` kind gets a `Keyword` implementation registered via `jsonschema::options().with_keyword(...)`. The validators check: -- **Numeric types** (`TypeDef:Float32`, `TypeDef:Int8`, etc.): range +- **Numeric types** (`AlkType:Float32`, `AlkType:Int8`, etc.): range constraints (Int8: -128..127, Uint8: 0..255, etc.), finiteness for floats. -- **`TypeDef:String`**: UTF-8 validity. -- **`TypeDef:Struct`**: field presence and types (delegated to +- **`AlkType:String`**: UTF-8 validity. +- **`AlkType:Struct`**: field presence and types (delegated to jsonschema's structural validation — the custom keyword only needs to - validate that the struct's fields match their declared `TypeDef:*` + validate that the struct's fields match their declared `AlkType:*` kinds). -- **`TypeDef:Union`**: discriminator value membership in the mapping. -- **`TypeDef:Array`**: element type conformance. -- **`TypeDef:Boolean`**: value is `true` or `false`. -- **`TypeDef:Timestamp`**: RFC 3339 string format (the internet profile of ISO 8601). +- **`AlkType:Union`**: discriminator value membership in the mapping. +- **`AlkType:Array`**: element type conformance. +- **`AlkType:Boolean`**: value is `true` or `false`. +- **`AlkType:Timestamp`**: RFC 3339 string format (the internet profile of ISO 8601). The `jsonschema` crate handles all the structural validation (object properties, required fields, array items, enum values) — the custom @@ -108,7 +108,7 @@ Read/write errors include the field path for debugging: ```rust // Example: reading a u32 from a buffer that's too short -Err(TypedefError::Access { +Err(AlkTypeError::Access { field_path: "header.version".to_string(), reason: "buffer too short: need 4 bytes at offset 12, have 2".to_string(), }) @@ -121,7 +121,7 @@ you exactly which field failed and why. ### Positive -- **Single error type.** Consumers handle one `TypedefError` enum, not +- **Single error type.** Consumers handle one `AlkTypeError` enum, not multiple error types from different engine phases. - **Field-path-carrying errors.** Read/write errors include the field path, making binary format debugging tractable. diff --git a/docs/architecture/decisions/005-int64-uint64-first-class-kinds.md b/docs/architecture/decisions/005-int64-uint64-first-class-kinds.md index 30f6c3e..db6b9fd 100644 --- a/docs/architecture/decisions/005-int64-uint64-first-class-kinds.md +++ b/docs/architecture/decisions/005-int64-uint64-first-class-kinds.md @@ -5,17 +5,17 @@ Accepted ## Context -The typedef engine's kind set (ADR-001, ADR-003) tops out at 32-bit +The alktype engine's kind set (ADR-001, ADR-003) tops out at 32-bit integers. The POC included `u64` read/write primitives, and the call-channels-unification research's own SFTP schema example uses -`"TypeDef:Uint64"` for the `offset` field (`Read`/`Write` packets have +`"AlkType:Uint64"` for the `offset` field (`Read`/`Write` packets have `offset: u64`). Metatensor/safetensors `data_offsets` are also `u64`. -A `TypeDef:Uint64` variant was added to the `TypeDefKind` enum during +A `AlkType:Uint64` variant was added to the `AlkTypeKind` enum during implementation (the task decomposition correctly identified the gap), but without an ADR the addition was half-finished: `type_size()` returned `None`, the layout engines couldn't compute offsets for it, and -the validator didn't register a `TypeDef:Uint64` keyword. The variant +the validator didn't register a `AlkType:Uint64` keyword. The variant was then removed (commit `14d9cf2`) on the grounds that it was unintended and a latent panic — but the underlying gap is real: SFTP and metatensor, the two primary POC targets, both require 64-bit integers. @@ -28,11 +28,11 @@ is 8 raw bytes, and `from_le_bytes`/`from_be_bytes` work correctly for the full `u64`/`i64` range. The validation concern is handled by accepting integer-form JSON values (the `jsonschema` crate's `as_i64`/`as_u64` methods handle the common range; values past 2^53 are -a JSON representation limitation, not a typedef limitation). +a JSON representation limitation, not a alktype limitation). ## Decision -**Add `TypeDef:Int64` and `TypeDef:Uint64` as first-class kinds.** +**Add `AlkType:Int64` and `AlkType:Uint64` as first-class kinds.** Both are fixed-size (8 bytes), with natural alignment 8. They follow the schema's endianness annotation like all other fixed-size types. @@ -43,20 +43,20 @@ Read/write is via `data_access::read_i64`/`write_i64`/`read_u64`/ | Kind | TypeBox key | Rust type | Size | Alignment | |------|-------------|-----------|------|-----------| -| `TInt64` | `TypeDef:Int64` | `i64` | 8 | 8 | -| `TUint64` | `TypeDef:Uint64` | `u64` | 8 | 8 | +| `TInt64` | `AlkType:Int64` | `i64` | 8 | 8 | +| `TUint64` | `AlkType:Uint64` | `u64` | 8 | 8 | ### Validation The custom keyword validators check: -- `TypeDef:Int64`: value must be an integer in `i64::MIN..=i64::MAX` +- `AlkType:Int64`: value must be an integer in `i64::MIN..=i64::MAX` (`-9223372036854775808` to `9223372036854775807`). -- `TypeDef:Uint64`: value must be a non-negative integer in +- `AlkType:Uint64`: value must be a non-negative integer in `0..=u64::MAX` (`0` to `18446744073709551615`). The `jsonschema` crate's `as_i64`/`as_u64` handle the common range. JSON numbers past 2^53 lose precision in the JSON representation — -this is a JSON limitation, not a typedef limitation. The binary +this is a JSON limitation, not a alktype limitation. The binary representation (8 raw bytes) is always exact. A consumer that needs to validate the full 64-bit range from JSON should provide the value as a JSON integer (which `serde_json` preserves for values up to @@ -66,13 +66,13 @@ enabled, or when the value fits in `i64`/`u64` without the feature). ### `FieldValue` additions `FieldValue::I64(i64)` and `FieldValue::U64(u64)` are added to the -unified return type. The `SequentialReader`, `TypedefEngine::read_field`, -and `TypedefEngine::write_field` dispatch on the new kinds. +unified return type. The `SequentialReader`, `AlkTypeEngine::read_field`, +and `AlkTypeEngine::write_field` dispatch on the new kinds. ### Kind count The engine now has **19** first-class kinds (17 + Int64 + Uint64). -`TypeDefKind::is_fixed_size()` returns `true` for both new kinds. +`AlkTypeKind::is_fixed_size()` returns `true` for both new kinds. `type_size()` returns `Some(8)`. `natural_alignment()` returns `8`. `needs_endian()` returns `true`. @@ -82,8 +82,8 @@ The engine now has **19** first-class kinds (17 + Int64 + Uint64). - **Unblocks the two primary POC targets.** SFTP `Read`/`Write` packets (`offset: u64`) and metatensor `data_offsets` (`u64`) are now - expressible in typedef schemas. -- **Completes the half-finished addition.** The `TypeDefKind` enum, + expressible in alktype schemas. +- **Completes the half-finished addition.** The `AlkTypeKind` enum, `data_access` primitives, and `FieldValue` variants for 64-bit integers now have matching layout, validator, and engine support. - **No new design surface.** Int64/Uint64 are fixed-size types that @@ -94,7 +94,7 @@ The engine now has **19** first-class kinds (17 + Int64 + Uint64). - **JSON precision caveat.** Values past 2^53 lose precision in the JSON representation (not in the binary representation). This is a - JSON limitation, not a typedef limitation, but it means the + JSON limitation, not a alktype limitation, but it means the validation layer cannot perfectly round-trip the full 64-bit range through JSON `Number` without `arbitrary_precision`. In practice, SFTP offsets and tensor data offsets are well within 2^53. @@ -105,7 +105,7 @@ The engine now has **19** first-class kinds (17 + Int64 + Uint64). ## References - `docs/research/call-channels-unification/findings.md` §"russh-sftp" — - the SFTP schema with `"offset": { "TypeDef:Uint64": true }` + the SFTP schema with `"offset": { "AlkType:Uint64": true }` - `docs/research/alknet-typedef/findings.md` §"POC 1" — the POC included u64 read/write - [ADR-001](001-alktype-purpose-scope-jsonschema-engine.md) — diff --git a/docs/architecture/decisions/006-reject-non-final-inline-length-prefixed-in-aligned-mode.md b/docs/architecture/decisions/006-reject-non-final-inline-length-prefixed-in-aligned-mode.md index d36c3f9..aa04f4f 100644 --- a/docs/architecture/decisions/006-reject-non-final-inline-length-prefixed-in-aligned-mode.md +++ b/docs/architecture/decisions/006-reject-non-final-inline-length-prefixed-in-aligned-mode.md @@ -17,7 +17,7 @@ indirection)." The implementation has a bug: `OffsetMap::compute` reserves only 4 bytes for an inline length-prefixed variable field (the length prefix), but -`TypedefEngine::write_field` for a `String`/`Bytes` field calls +`AlkTypeEngine::write_field` for a `String`/`Bytes` field calls `data_access::write_string` at `range.start`, which writes `[4-byte length][data]` inline — clobbering every subsequent field. The `read_field` path has the mirror behavior (reads inline), so the engine @@ -45,12 +45,12 @@ length-prefixing. **Reject non-final inline length-prefixed variable fields in aligned static mode at `OffsetMap::compute` time.** -A variable-length field (`TypeDef:String`, `TypeDef:Bytes`, -`TypeDef:Timestamp`, `TypeDef:Record`) in aligned static mode that uses +A variable-length field (`AlkType:String`, `AlkType:Bytes`, +`AlkType:Timestamp`, `AlkType:Record`) in aligned static mode that uses the default inline length-prefixing strategy (no `maxLength`, no `offset-indirect`) must be the last field in its struct. If a non-final inline length-prefixed variable field is encountered, -`OffsetMap::compute` returns `TypedefError::Offset` with a message +`OffsetMap::compute` returns `AlkTypeError::Offset` with a message explaining that non-final variable fields in aligned mode require `maxLength` (fixed-size reservation) or `"encoding": "offset-indirect"` (offset indirection). diff --git a/docs/architecture/decisions/007-packed-mode-read-factory.md b/docs/architecture/decisions/007-packed-mode-read-factory.md index 8a493e8..cf2822e 100644 --- a/docs/architecture/decisions/007-packed-mode-read-factory.md +++ b/docs/architecture/decisions/007-packed-mode-read-factory.md @@ -5,7 +5,7 @@ Accepted ## Context -`TypedefEngine` stores a `SequentialReader` inside its `Layout::Packed` +`AlkTypeEngine` stores a `SequentialReader` inside its `Layout::Packed` variant. The engine exposes it via `engine.sequential_reader() -> Option<&SequentialReader>`. @@ -43,7 +43,7 @@ Three options were considered: an owned fresh reader, reconstructed from the stored schema. ```rust -impl TypedefEngine { +impl AlkTypeEngine { /// Construct a fresh SequentialReader for packed-mode reads. /// Returns None if compiled in aligned mode. pub fn sequential_reader(&self) -> Option; diff --git a/docs/architecture/decisions/008-reject-tunion-in-aligned-mode.md b/docs/architecture/decisions/008-reject-tunion-in-aligned-mode.md index 6303343..0468ca5 100644 --- a/docs/architecture/decisions/008-reject-tunion-in-aligned-mode.md +++ b/docs/architecture/decisions/008-reject-tunion-in-aligned-mode.md @@ -12,7 +12,7 @@ aligned mode has three implementation problems: 1. **No variant field offsets.** Only the `__discriminator` byte range is recorded in the `OffsetMap`. Variant field offsets are not available anywhere in aligned mode — the consumer must recompute - them by hand. This makes `TypedefEngine::read_field` on a union + them by hand. This makes `AlkTypeEngine::read_field` on a union variant field impossible. 2. **`find_discriminator_field` takes the first variant's offset.** For @@ -49,8 +49,8 @@ worse than rejecting it clearly. **Reject `TUnion` in aligned static mode for v1.** -`OffsetMap::compute` returns `TypedefError::Offset` when it encounters -a `TypeDef:Union` field, with a message explaining that unions are not +`OffsetMap::compute` returns `AlkTypeError::Offset` when it encounters +a `AlkType:Union` field, with a message explaining that unions are not supported in aligned mode and the consumer should use packed mode (or restructure as a struct with an explicit discriminator field). diff --git a/docs/architecture/layout-engine.md b/docs/architecture/layout-engine.md index b16cf3c..9a06baf 100644 --- a/docs/architecture/layout-engine.md +++ b/docs/architecture/layout-engine.md @@ -24,8 +24,8 @@ protocols. **Components:** -- **`LayoutBuilder`** — constructed via `LayoutBuilder::new(schema)` (requires `TypeDef:Struct` at the top level), then `builder.build(&var_sizes) -> Result` where `var_sizes: &HashMap` maps variable-length field paths (and TUnion discriminator/variant keys) to their actual byte sizes. Used at write time when the consumer knows the data sizes upfront. The builder computes positions only; the consumer writes data via the [`data_access`](data-access.md) functions at the computed positions. -- **`SequentialReader`** — constructed via `SequentialReader::new(schema)`, then driven by `reader.read_next(&buffer) -> Result, TypedefError>` until `Ok(None)`, or `reader.read_field(&buffer, path)` to seek a single field (which walks all preceding fields to reach the target). `reader.reset()` rewinds to the start. Used at read time when the consumer is parsing an incoming frame. +- **`LayoutBuilder`** — constructed via `LayoutBuilder::new(schema)` (requires `AlkType:Struct` at the top level), then `builder.build(&var_sizes) -> Result` where `var_sizes: &HashMap` maps variable-length field paths (and TUnion discriminator/variant keys) to their actual byte sizes. Used at write time when the consumer knows the data sizes upfront. The builder computes positions only; the consumer writes data via the [`data_access`](data-access.md) functions at the computed positions. +- **`SequentialReader`** — constructed via `SequentialReader::new(schema)`, then driven by `reader.read_next(&buffer) -> Result, AlkTypeError>` until `Ok(None)`, or `reader.read_field(&buffer, path)` to seek a single field (which walks all preceding fields to reach the target). `reader.reset()` rewinds to the start. Used at read time when the consumer is parsing an incoming frame. **How it works:** @@ -69,7 +69,7 @@ and safetensors. **Component:** -- **`OffsetMap`** — constructed via `OffsetMap::compute(schema) -> Result` (requires `TypeDef:Struct` at the top level). Walks the schema once, computes fixed byte positions for each field based on type sizes and alignment. The output is a flat table of `(field_path, byte_range)` pairs (see [Public Types](#public-types)). Used for both read and write at known offsets. +- **`OffsetMap`** — constructed via `OffsetMap::compute(schema) -> Result` (requires `AlkType:Struct` at the top level). Walks the schema once, computes fixed byte positions for each field based on type sizes and alignment. The output is a flat table of `(field_path, byte_range)` pairs (see [Public Types](#public-types)). Used for both read and write at known offsets. **How it works:** @@ -109,7 +109,7 @@ length, then slices the separate data region. Inline length-prefixed variable fields in aligned mode are only allowed as the **last field** in their struct. A non-final inline length-prefixed variable field is rejected at `OffsetMap::compute` time -with a `TypedefError::Offset` — the `OffsetMap` reserves only 4 bytes +with a `AlkTypeError::Offset` — the `OffsetMap` reserves only 4 bytes (the length prefix), but `data_access::write_string` writes prefix + data inline, which would clobber subsequent fields. Non-final variable fields must use `maxLength` (fixed-size reservation) or @@ -125,7 +125,7 @@ padding is inserted between fields. ### Fixed-size types For each fixed-size type, the algorithm: -1. Determines the type's byte size from the `TypeDef:*` kind. +1. Determines the type's byte size from the `AlkType:*` kind. 2. In aligned mode: inserts padding to satisfy the type's alignment (or the field's `align` annotation, or the struct's `align` default). 3. Records the field's `(start, end)` range. @@ -141,7 +141,7 @@ its total size. **`TUnion`:** TUnion is supported in packed sequential mode only. In aligned static mode, `OffsetMap::compute` rejects `TUnion` fields with -`TypedefError::Offset` — see +`AlkTypeError::Offset` — see [ADR-008](decisions/008-reject-tunion-in-aligned-mode.md). Unions are the protocol dispatch pattern (SFTP type bytes, call protocol event types); mmap-friendly formats use structs and arrays, not tagged unions. @@ -170,7 +170,7 @@ alignment padding in aligned mode). Element `i` starts at ### Variable-length types -The typedef engine supports three strategies for variable-length types +The alktype engine supports three strategies for variable-length types (see [schema-layer.md](schema-layer.md) §Variable-length types and [ADR-003](decisions/003-schema-annotations.md) §3 for the full annotation shapes). @@ -221,7 +221,7 @@ and byte-swaps accordingly. All fixed-size types — including `TEnum` ## Mode Selection The consumer selects the mode at engine construction time via the -`LayoutMode` enum, passed to `TypedefEngine::compile`: +`LayoutMode` enum, passed to `AlkTypeEngine::compile`: ```rust pub enum LayoutMode { @@ -246,7 +246,7 @@ frames) and a `LayoutBuilder` (for constructing outgoing frames). A schema describing a metatensor layout can be consumed by an `OffsetMap` (for mmap access). -`TypedefEngine` exposes mode-appropriate accessors: `engine.offset_map()` +`AlkTypeEngine` exposes mode-appropriate accessors: `engine.offset_map()` returns `Some(&OffsetMap)` in aligned mode and `None` in packed mode; `engine.layout_builder()` returns `Some(&LayoutBuilder)` in packed mode and `None` in aligned mode. `engine.sequential_reader()` returns @@ -254,7 +254,7 @@ and `None` in aligned mode. `engine.sequential_reader()` returns reader has mutable cursor state that the consumer owns; see [ADR-007](decisions/007-packed-mode-read-factory.md)) in packed mode and `None` in aligned mode. See [validation.md](validation.md) -§"The TypedefEngine struct" for the engine API. +§"The AlkTypeEngine struct" for the engine API. ## Public Types @@ -282,14 +282,14 @@ provides `len()` and `is_empty()`. pub struct FieldPosition { pub offset: usize, pub size: usize, - pub kind: TypeDefKind, + pub kind: AlkTypeKind, } ``` A field's computed position in a packed layout, produced by `LayoutBuilder::build`. For variable-length fields, `size` is `4` (the length prefix); for fixed-size fields, `size` is the type's byte size. -`kind` records the field's `TypeDef:*` kind so the consumer can dispatch +`kind` records the field's `AlkType:*` kind so the consumer can dispatch to the correct `data_access` read/write function. ### `PackedLayout` (packed mode) @@ -317,14 +317,14 @@ A flat table of `(field_path, byte_range)` pairs computed from a schema. ```rust impl OffsetMap { - pub fn compute(schema: &Value) -> Result; + pub fn compute(schema: &Value) -> Result; pub fn get(&self, field_path: &str) -> Option<&ByteRange>; pub fn total_size(&self) -> usize; pub fn iter(&self) -> impl Iterator; } ``` -`compute` requires a `TypeDef:Struct` at the top level. `total_size` +`compute` requires a `AlkType:Struct` at the top level. `total_size` includes trailing alignment padding. `iter` yields fields in insertion order (schema `properties` order, nested struct fields appearing inline). @@ -354,7 +354,7 @@ See [open-questions.md](open-questions.md) for full details. the two layout modes decision - [ADR-003](decisions/003-schema-annotations.md) — schema annotations -- [schema-layer.md](schema-layer.md) — the 17 TypeDef kinds and their +- [schema-layer.md](schema-layer.md) — the 17 AlkType kinds and their byte sizes - [data-access.md](data-access.md) — read/write functions that use the computed offsets diff --git a/docs/architecture/overview.md b/docs/architecture/overview.md index 8357251..c71b8ec 100644 --- a/docs/architecture/overview.md +++ b/docs/architecture/overview.md @@ -6,7 +6,7 @@ last_updated: 2026-07-22 # alktype — Overview The binary struct engine: a small Rust crate that takes a JSON Schema -with `TypeDef:*` custom keywords and produces an offset map, read/write +with `AlkType:*` custom keywords and produces an offset map, read/write functions, and validation — all driven by the schema. The schema is the format definition; the engine is generic. @@ -17,8 +17,8 @@ Component details are in the sibling documents. ## What `alktype` is a library crate that consumes JSON Schemas annotated -with `TypeDef:*` custom keywords (the same kinds defined in TypeBox's -`typedef.ts`, plus `TypeDef:Bytes`, `TypeDef:Int64`, and `TypeDef:Uint64` +with `AlkType:*` custom keywords (the same kinds defined in TypeBox's +`typedef.ts`, plus `AlkType:Bytes`, `AlkType:Int64`, and `AlkType:Uint64` as alktype additions) and produces three capabilities: 1. **An offset map** — walks the schema, computes byte offsets for each @@ -52,8 +52,8 @@ engine with different schemas. The guiding insight: -> **The schema is the format.** A JSON Schema with `TypeDef:Float32`, -> `TypeDef:Struct`, `TypeDef:Union` etc. is both the validation spec and +> **The schema is the format.** A JSON Schema with `AlkType:Float32`, +> `AlkType:Struct`, `AlkType:Union` etc. is both the validation spec and > the layout spec. No separate format definition, no separate parser, no > separate validator. One schema, three uses: validate, compute offsets, > access data. @@ -67,15 +67,15 @@ the binary data is the struct's bytes at computed offsets. The crate was bumped up in the timeline when the call-channels-unification research surfaced that channels, TTY, and the binary call protocol are all variations on the same wire-format family — `[discriminant][length][payload]`. -The typedef engine makes the "channels is call with a binary data plane" +The alktype engine makes the "channels is call with a binary data plane" unification concrete: the binary data plane's wire format is the call protocol's own schema system, just binary-encoded. The `channel_open` -marker says "use binary framing"; the typedef engine says "here's how to +marker says "use binary framing"; the alktype engine says "here's how to read/write the binary payload." ## The "Schema Is the Format" Principle -A JSON Schema with `TypeDef:*` custom keywords serves three roles +A JSON Schema with `AlkType:*` custom keywords serves three roles simultaneously: | Role | Mechanism | When | @@ -123,7 +123,7 @@ schema JSON determines the order of fields in the binary struct. The russh-sftp case is the most instructive and the highest-value POC target. The `Packet` enum's `TryFrom<&mut Bytes>` impl is a hand-written -dispatch on a type byte followed by serde deserialization. Under typedef, +dispatch on a type byte followed by serde deserialization. Under alktype, the dispatch is `TUnion` with a byte-offset discriminator — the schema says "byte 0 is the discriminator, bytes 1..N are the variant struct." The engine reads the discriminator, looks up the variant schema, computes @@ -133,28 +133,28 @@ offsets, reads fields. Same result, no per-packet-type code. These boundaries are decided in [ADR-001](decisions/001-alktype-purpose-scope-jsonschema-engine.md). -- **Not metatensor.** typedef is the binary struct *engine*. Metatensor +- **Not metatensor.** alktype is the binary struct *engine*. Metatensor is a *format* (8-byte header + JSON header + binary data) that uses the - typedef engine for its offset computation and tensor access. + alktype engine for its offset computation and tensor access. - **Not a Value system.** TypeBox's `Value.Diff`, `Value.Migrate`, `Value.Convert` — schema evolution — is out of scope for v1. The engine should not do anything that explicitly blocks adding a Value system later. - **Not a code generator.** typebox-rs's `codegen/` module is a separate - concern. The typedef engine consumes schemas; it does not generate them. -- **Not a schema builder.** The typedef engine does not provide a fluent + concern. The alktype engine consumes schemas; it does not generate them. +- **Not a schema builder.** The alktype engine does not provide a fluent API for constructing schemas. Schemas are plain JSON — authored in TypeBox, generated by ujsx components, or hand-written. A builder API is deferred (OQ-003). -- **Not a serialization framework.** The typedef engine is not a +- **Not a serialization framework.** The alktype engine is not a general-purpose serde replacement. It operates on raw byte buffers at computed offsets — no intermediate `Value` tree, no reflection, no dynamic dispatch per field. For JSON data, use serde. For binary data - with a known schema, use typedef. + with a known schema, use alktype. ## Architecture (component pointers) -- **[schema-layer.md](schema-layer.md)** — the 19 `TypeDef:*` kinds, +- **[schema-layer.md](schema-layer.md)** — the 19 `AlkType:*` kinds, jsonschema custom keyword integration, TypeBox interop, schema annotations (endianness, alignment, encoding, TUnion discriminators). - **[layout-engine.md](layout-engine.md)** — offset computation, the two @@ -164,8 +164,8 @@ These boundaries are decided in [ADR-001](decisions/001-alktype-purpose-scope-js dispatch, field paths, zero-copy access for fixed-size types, length-prefix reading for variable-length types. - **[validation.md](validation.md)** — custom keyword validators for all - 19 `TypeDef:*` kinds, `TypedefError`, load-time vs access-time - validation, `TypedefEngine` as the compiled form of a schema. + 19 `AlkType:*` kinds, `AlkTypeError`, load-time vs access-time + validation, `AlkTypeEngine` as the compiled form of a schema. ## Design Decisions @@ -174,7 +174,7 @@ These boundaries are decided in [ADR-001](decisions/001-alktype-purpose-scope-js | Purpose, scope, and the jsonschema engine | [ADR-001](decisions/001-alktype-purpose-scope-jsonschema-engine.md) | What the crate is/isn't; why jsonschema not a custom engine; "schema is the format" principle; scope boundaries | | Two layout modes | [ADR-002](decisions/002-two-layout-modes-packed-vs-aligned.md) | Packed sequential (`LayoutBuilder`/`SequentialReader`) for protocols; aligned static (`OffsetMap`) for mmap formats | | Schema annotations | [ADR-003](decisions/003-schema-annotations.md) | Endianness (schema-level, default LE), alignment (struct + field-level), encoding (length-prefixed vs offset-indirect), TUnion discriminators (byte-offset vs field-name) | -| Error handling and validation | [ADR-004](decisions/004-error-handling-validation-strategy.md) | `TypedefError` enum; load-time build, access-time check; field-path-carrying errors; jsonschema `ValidationError` wrapping | +| Error handling and validation | [ADR-004](decisions/004-error-handling-validation-strategy.md) | `AlkTypeError` enum; load-time build, access-time check; field-path-carrying errors; jsonschema `ValidationError` wrapping | | Int64/Uint64 kinds | [ADR-005](decisions/005-int64-uint64-first-class-kinds.md) | 64-bit integers as first-class kinds (SFTP offsets, metatensor data_offsets) | | Non-final inline variable fields | [ADR-006](decisions/006-reject-non-final-inline-length-prefixed-in-aligned-mode.md) | Rejected in aligned mode (would clobber subsequent fields) | | Packed-mode read factory | [ADR-007](decisions/007-packed-mode-read-factory.md) | `engine.sequential_reader()` returns an owned fresh reader | @@ -199,5 +199,5 @@ See [open-questions.md](open-questions.md) for full details. schema kinds (619 lines) - `/workspace/jsonschema/` — the jsonschema crate (v0.46.5, Draft 2020-12) - `/workspace/alknet-typedef-poc/` — the POC code (disposable) -- `/workspace/@alkimiadev/typebox-rs/` — prior attempt, replaced by typedef +- `/workspace/@alkimiadev/typebox-rs/` — prior attempt, replaced by alktype - `/workspace/@alkimiadev/alktype/` — prior attempt (the @alkimiadev/alktype prototype; not to be confused with this crate, which reuses the name but is backed by the `jsonschema` crate) diff --git a/docs/architecture/questions/003-builder-api-for-schema-construction.md b/docs/architecture/questions/003-builder-api-for-schema-construction.md index 75df28e..4081def 100644 --- a/docs/architecture/questions/003-builder-api-for-schema-construction.md +++ b/docs/architecture/questions/003-builder-api-for-schema-construction.md @@ -9,7 +9,7 @@ changing the existing JSON-consumption path) - **Priority**: medium - **Impacts**: Blocks programmatic schema construction in Rust without a - JS toolchain. Any consumer that wants to build typedef schemas at + JS toolchain. Any consumer that wants to build alktype schemas at runtime from Rust code (rather than loading pre-authored JSON) must construct the JSON manually or depend on TypeBox. Does NOT block any current consumer — all v1 consumers (SFTP, metatensor, binary call diff --git a/docs/architecture/schema-layer.md b/docs/architecture/schema-layer.md index bb57a0e..53e81b8 100644 --- a/docs/architecture/schema-layer.md +++ b/docs/architecture/schema-layer.md @@ -5,12 +5,12 @@ last_updated: 2026-07-22 # alktype — Schema Layer -The schema layer: the 19 `TypeDef:*` custom type kinds, their mapping to +The schema layer: the 19 `AlkType:*` custom type kinds, their mapping to Rust types and byte sizes, the `jsonschema` custom keyword integration, TypeBox interop, and the concrete JSON shapes for schema-level annotations. -## The 19 TypeDef Kinds +## The 19 AlkType Kinds These are the custom schema kinds defined in TypeBox's `typedef.ts` (`/workspace/@alkdev/typebox/example/typedef/typedef.ts`, 619 lines) and @@ -20,42 +20,42 @@ encoding strategy (for variable-length types). | Kind | TypeBox key | Rust type | Size | Category | |------|-------------|-----------|------|----------| -| `TFloat32` | `TypeDef:Float32` | `f32` | 4 | fixed | -| `TFloat64` | `TypeDef:Float64` | `f64` | 8 | fixed | -| `TInt8` | `TypeDef:Int8` | `i8` | 1 | fixed | -| `TInt16` | `TypeDef:Int16` | `i16` | 2 | fixed | -| `TInt32` | `TypeDef:Int32` | `i32` | 4 | fixed | -| `TInt64` | `TypeDef:Int64` | `i64` | 8 | fixed | -| `TUint8` | `TypeDef:Uint8` | `u8` | 1 | fixed | -| `TUint16` | `TypeDef:Uint16` | `u16` | 2 | fixed | -| `TUint32` | `TypeDef:Uint32` | `u32` | 4 | fixed | -| `TUint64` | `TypeDef:Uint64` | `u64` | 8 | fixed | -| `TBoolean` | `TypeDef:Boolean` | `bool` (0x00=false, 0x01=true) | 1 | fixed | -| `TString` | `TypeDef:String` | length-prefixed UTF-8 | variable | variable | -| `TBytes` | `TypeDef:Bytes` | length-prefixed raw bytes | variable | variable | -| `TStruct` | `TypeDef:Struct` | record of fields | sum of field sizes | composite | -| `TUnion` | `TypeDef:Union` | tagged union | discriminator + variant | composite | -| `TArray` | `TypeDef:Array` | repeated element | count × element size | composite | -| `TEnum` | `TypeDef:Enum` | u32 index into enum values | 4 (fixed) | fixed | -| `TRecord` | `TypeDef:Record` | count-prefixed sequence of (key, value) pairs | variable | variable | -| `TTimestamp` | `TypeDef:Timestamp` | length-prefixed RFC 3339 string | variable | variable | +| `TFloat32` | `AlkType:Float32` | `f32` | 4 | fixed | +| `TFloat64` | `AlkType:Float64` | `f64` | 8 | fixed | +| `TInt8` | `AlkType:Int8` | `i8` | 1 | fixed | +| `TInt16` | `AlkType:Int16` | `i16` | 2 | fixed | +| `TInt32` | `AlkType:Int32` | `i32` | 4 | fixed | +| `TInt64` | `AlkType:Int64` | `i64` | 8 | fixed | +| `TUint8` | `AlkType:Uint8` | `u8` | 1 | fixed | +| `TUint16` | `AlkType:Uint16` | `u16` | 2 | fixed | +| `TUint32` | `AlkType:Uint32` | `u32` | 4 | fixed | +| `TUint64` | `AlkType:Uint64` | `u64` | 8 | fixed | +| `TBoolean` | `AlkType:Boolean` | `bool` (0x00=false, 0x01=true) | 1 | fixed | +| `TString` | `AlkType:String` | length-prefixed UTF-8 | variable | variable | +| `TBytes` | `AlkType:Bytes` | length-prefixed raw bytes | variable | variable | +| `TStruct` | `AlkType:Struct` | record of fields | sum of field sizes | composite | +| `TUnion` | `AlkType:Union` | tagged union | discriminator + variant | composite | +| `TArray` | `AlkType:Array` | repeated element | count × element size | composite | +| `TEnum` | `AlkType:Enum` | u32 index into enum values | 4 (fixed) | fixed | +| `TRecord` | `AlkType:Record` | count-prefixed sequence of (key, value) pairs | variable | variable | +| `TTimestamp` | `AlkType:Timestamp` | length-prefixed RFC 3339 string | variable | variable | -`TypeDef:Int64` and `TypeDef:Uint64` are alktype additions — +`AlkType:Int64` and `AlkType:Uint64` are alktype additions — TypeBox's `typedef.ts` tops out at 32-bit integers. They are required by the primary POC targets: SFTP `Read`/`Write` packets have `offset: u64`, and metatensor `data_offsets` are `u64`. See [ADR-005](decisions/005-int64-uint64-first-class-kinds.md). -### The `TypeDefKind` enum +### The `AlkTypeKind` enum -The engine represents the 19 kinds as a Rust enum — `TypeDefKind` — with -one variant per kind (`TypeDefKind::Float32`, `TypeDefKind::Struct`, etc.). +The engine represents the 19 kinds as a Rust enum — `AlkTypeKind` — with +one variant per kind (`AlkTypeKind::Float32`, `AlkTypeKind::Struct`, etc.). The enum provides compile-time exhaustiveness checking and integer discriminant dispatch (a jump table) instead of string comparison at every field access. It is `pub` and re-exported from the crate root. ```rust -pub enum TypeDefKind { +pub enum AlkTypeKind { Int8, Int16, Int32, Int64, Uint8, Uint16, Uint32, Uint64, Float32, Float64, @@ -69,7 +69,7 @@ The enum carries the kind's binary-layout metadata as inherent methods: | Method | Returns | Notes | |--------|---------|-------| -| `as_str(self)` | `&'static str` | The JSON Schema keyword, e.g. `"TypeDef:Uint8"` | +| `as_str(self)` | `&'static str` | The JSON Schema keyword, e.g. `"AlkType:Uint8"` | | `type_size(self)` | `Option` | `Some(N)` for fixed-size kinds; `None` for variable/composite | | `natural_alignment(self)` | `usize` | 1 for u8/i8/bool, 2 for u16/i16, 4 for u32/i32/f32/enum, 8 for u64/i64/f64, 4 for variable-length (the u32 length prefix), 1 for struct/union/array | | `is_fixed_size(self)` | `bool` | True for the 12 fixed-size primitive kinds | @@ -77,9 +77,9 @@ The enum carries the kind's binary-layout metadata as inherent methods: | `is_variable_length(self)` | `bool` | True for String, Bytes, Timestamp, Record | | `needs_endian(self)` | `bool` | True for kinds whose read/write takes an `Endian` parameter | -`TypeDefKind` implements `Display` (renders the keyword string) and +`AlkTypeKind` implements `Display` (renders the keyword string) and `FromStr` (parses the keyword string back into the variant, returning -`TypedefError::Schema` for unknown kinds). The layout engines and the +`AlkTypeError::Schema` for unknown kinds). The layout engines and the validator dispatch on the enum, not on strings. ### Fixed-size types @@ -90,17 +90,17 @@ computation uses these sizes directly. Read/write is zero-copy pointer cast for these types. **`TBoolean` byte representation:** `0x00` = false, `0x01` = true. Other -values are invalid and produce a `TypedefError::Access` on read. +values are invalid and produce a `AlkTypeError::Access` on read. **`TEnum` binary representation:** A `u32` index into the enum's declared values, in declaration order. The first declared value is index 0, the second is index 1, etc. The enum's values are declared via the standard JSON Schema `"enum"` keyword (e.g., `"enum": ["read", "write", "execute"]`). -The `TypeDef:Enum` custom keyword signals that the type is an enum for +The `AlkType:Enum` custom keyword signals that the type is an enum for layout purposes; the built-in `enum` keyword provides the value list. **Design note:** TypeBox's `TEnum` is a string enum (variable-length). The -typedef engine uses a `u32` index instead — a deliberate deviation from +alktype engine uses a `u32` index instead — a deliberate deviation from TypeBox fidelity in favor of binary efficiency. Most enums have a small number of variants (e.g., the call protocol's 5 event types); a `u32` index is compact, fixed-size, and sufficient for any realistic enum. The @@ -113,7 +113,7 @@ all other fixed-size types. In little-endian mode the index is ### Variable-length types `TString`, `TBytes`, `TRecord`, and `TTimestamp` have variable byte sizes. -The typedef engine supports three strategies for handling variable-length +The alktype engine supports three strategies for handling variable-length types in binary layouts, selected by the `encoding` annotation and the standard JSON Schema `maxLength` keyword: @@ -169,7 +169,7 @@ consistent byte order for both field values and length prefixes. **`TBytes`:** Raw bytes — no UTF-8 constraint. The payload is `&[u8]`. Otherwise identical to `TString` in layout (same three strategies). -**Design note:** `TypeDef:Bytes` is an alktype addition — it does +**Design note:** `AlkType:Bytes` is an alktype addition — it does not exist in TypeBox's `typedef.ts` (which defines 16 kinds). It is included because raw byte arrays are a common binary protocol primitive (SFTP data payloads, channels payloads, tensor data) and are semantically @@ -178,11 +178,11 @@ bytes with no encoding (not base64, not hex). In the JSON representation (for validation), TBytes is a string (JSON has no native byte type). **`TRecord`:** A string-keyed map. The value type is declared via the -schema's `"values"` property (e.g., `"values": { "TypeDef:Float32": true }`). +schema's `"values"` property (e.g., `"values": { "AlkType:Float32": true }`). Binary layout is a count-prefixed sequence of `(key, value)` pairs: `[count: u32][key_len: u32][key_bytes][value]...` repeated `count` times. The count is the number of entries. Each key is a length-prefixed UTF-8 -string. Each value is encoded according to its declared `TypeDef:*` kind +string. Each value is encoded according to its declared `AlkType:*` kind — a `Record` value is 4 raw bytes; a `Record` value is itself a length-prefixed string; a `Record` value is the struct's fields laid out inline. There is **no separate `value_len` prefix** — @@ -222,25 +222,25 @@ computation recurses into their properties. The `schema` module exposes the foundational types and functions every other module depends on. These are re-exported from the crate root. -### `get_typedef_kind` vs `get_typedef_kind_loose` +### `get_alktype_kind` vs `get_alktype_kind_loose` -The engine recognizes a `TypeDef:*` kind on a schema node two ways, +The engine recognizes a `AlkType:*` kind on a schema node two ways, because the keyword value may be either a boolean (`true`) or an annotation object (`{ "encoding": "..." }`): | Function | Recognizes | Returns | |----------|------------|---------| -| `get_typedef_kind(node) -> Option<&str>` | Boolean form only (`{ "TypeDef:String": true }`) | The keyword string, e.g. `"TypeDef:String"` | -| `get_typedef_kind_loose(node) -> Option<&str>` | Boolean form **and** object form | The keyword string | -| `get_typedef_kind_enum(node) -> Option` | Boolean form only | The parsed enum variant | -| `get_typedef_kind_loose_enum(node) -> Option` | Boolean form **and** object form | The parsed enum variant | +| `get_alktype_kind(node) -> Option<&str>` | Boolean form only (`{ "AlkType:String": true }`) | The keyword string, e.g. `"AlkType:String"` | +| `get_alktype_kind_loose(node) -> Option<&str>` | Boolean form **and** object form | The keyword string | +| `get_alktype_kind_enum(node) -> Option` | Boolean form only | The parsed enum variant | +| `get_alktype_kind_loose_enum(node) -> Option` | Boolean form **and** object form | The parsed enum variant | The boolean-form-only functions are used by the validator factories (which reject the object form as a schema error) and the top-level kind-check in `OffsetMap::compute` / `LayoutBuilder::new` / `SequentialReader::new` -(which require `TypeDef:Struct` at the root). The "loose" variants are +(which require `AlkType:Struct` at the root). The "loose" variants are used by the layout engines during field traversal, so that a variable- -length field with an `encoding` annotation (`{ "TypeDef:String": +length field with an `encoding` annotation (`{ "AlkType:String": { "encoding": "offset-indirect" } }`) is still recognized as a `String`. ### Annotation parsers @@ -254,7 +254,7 @@ Each schema-level annotation has a dedicated parser that reads it from a | `parse_align(node) -> Option` | `"align"` | `None` | | `parse_max_length(node) -> Option` | `"maxLength"` | `None` | | `parse_encoding(keyword_value) -> VariableEncoding` | `"encoding"` (within the keyword's value object) | `VariableEncoding::LengthPrefixed` | -| `parse_discriminator(node) -> Result` | `"discriminator"` | (required — returns `TypedefError::Schema` if absent) | +| `parse_discriminator(node) -> Result` | `"discriminator"` | (required — returns `AlkTypeError::Schema` if absent) | ### Public enums @@ -262,13 +262,13 @@ Each schema-level annotation has a dedicated parser that reads it from a pub enum Endian { Little, Big } pub enum VariableEncoding { LengthPrefixed, OffsetIndirect } pub enum DiscriminatorKind { - Byte { offset: usize, disc_type: TypeDefKind }, + Byte { offset: usize, disc_type: AlkTypeKind }, Field { name: String }, } ``` `DiscriminatorKind::Byte` carries the byte position (`offset`) and the -discriminator's `TypeDef:*` kind (`disc_type`, restricted to `Uint8`/ +discriminator's `AlkType:*` kind (`disc_type`, restricted to `Uint8`/ `Uint16`/`Uint32`). `DiscriminatorKind::Field` carries the discriminator field's name. See [data-access.md](data-access.md) §"TUnion Dispatch" for how these drive dispatch. @@ -277,7 +277,7 @@ how these drive dispatch. | Function | Purpose | |----------|---------| -| `normalize_refs(schema: &mut Value)` | Walks the schema; rewrites every `"$ref"` whose value is a bare name (no `#` prefix) to `"#/$defs/"`. Idempotent. Runs once at `TypedefEngine::compile` time. | +| `normalize_refs(schema: &mut Value)` | Walks the schema; rewrites every `"$ref"` whose value is a bare name (no `#` prefix) to `"#/$defs/"`. Idempotent. Runs once at `AlkTypeEngine::compile` time. | | `resolve_ref(root, ref_path) -> Option<&Value>` | Resolves a JSON Pointer `$ref` (e.g. `"#/$defs/Read"`) against the root schema. | | `resolve_ref_or_inline(node, root) -> Option<&Value>` | If `node` has a `"$ref"`, resolves it against `root`; otherwise returns `node` itself (it's an inline schema). | @@ -288,22 +288,22 @@ on every `$ref`-bearing node they encounter during traversal. ## jsonschema Custom Keyword Integration The `jsonschema` crate (v0.46.5, Draft 2020-12) supports custom keywords -via the `with_keyword` API. Each `TypeDef:*` kind is registered as a +via the `with_keyword` API. Each `AlkType:*` kind is registered as a custom keyword: ```rust let validator = jsonschema::options() - .with_keyword("TypeDef:Float32", factory) - .with_keyword("TypeDef:Int32", factory) - .with_keyword("TypeDef:Struct", factory) + .with_keyword("AlkType:Float32", factory) + .with_keyword("AlkType:Int32", factory) + .with_keyword("AlkType:Struct", factory) // ... all 17 kinds .build(&schema)?; ``` The factory closure receives the parent schema object, the keyword's value, and the schema path — enabling cross-keyword awareness. The -`TypeDef:Struct` validator, for example, inspects the parent's -`properties` to validate each field against its declared `TypeDef:*` kind. +`AlkType:Struct` validator, for example, inspects the parent's +`properties` to validate each field against its declared `AlkType:*` kind. Each custom keyword implementation is ~10 lines. The `jsonschema` crate handles all structural validation (object properties, required fields, @@ -313,7 +313,7 @@ validator implementations. This is the same pattern as TypeBox's `TypeRegistry.Set` on the JS side. Same semantics, different language, same JSON Schema wire format. A -TypeBox schema serialized to JSON feeds into the typedef engine after a +TypeBox schema serialized to JSON feeds into the alktype engine after a single pre-processing step: normalizing `$ref` values (see below). ## TypeBox Interop @@ -330,8 +330,8 @@ const TensorRef = Type.Object({ ``` serialized to JSON is a standard JSON Schema with `type: "object"`, -`properties`, and `required`. That JSON feeds into the typedef engine -after `$ref` normalization. The `TypeDef:*` custom keywords are added by +`properties`, and `required`. That JSON feeds into the alktype engine +after `$ref` normalization. The `AlkType:*` custom keywords are added by TypeBox's `TypeRegistry.Set` — they appear in the serialized JSON as additional properties on the schema object. @@ -340,11 +340,11 @@ additional properties on the schema object. TypeBox generates bare-name `$ref` values (e.g., `"$ref": "Read"`), referencing sibling definitions within the same `$defs` block. The `jsonschema` crate requires full JSON Pointer paths (e.g., -`"$ref": "#/$defs/Read"`). The typedef engine normalizes TypeBox-style +`"$ref": "#/$defs/Read"`). The alktype engine normalizes TypeBox-style refs at schema load time via [`normalize_refs`](#ref-resolution-and-normalization) — a ~20-line recursive walk that rewrites every bare-name `"$ref"` to `"#/$defs/"`. The normalization is idempotent — full JSON Pointer -refs pass through unchanged. It runs once at `TypedefEngine::compile` +refs pass through unchanged. It runs once at `AlkTypeEngine::compile` time, before the schema is passed to `jsonschema` or the offset computation. @@ -353,7 +353,7 @@ with `Resource 'Read' is not present in a registry`. Full JSON Pointer refs (`#/$defs/Read`) resolve correctly. The normalization step bridges the gap between TypeBox's output and jsonschema's input. -The typedef engine does not depend on TypeBox or any JS toolchain. It +The alktype engine does not depend on TypeBox or any JS toolchain. It consumes JSON — whether that JSON was authored in TypeBox, generated by a ujsx component, or hand-written. The schema is the interface. @@ -367,7 +367,7 @@ decided in [ADR-003](decisions/003-schema-annotations.md). Schema-level annotation with a default of little-endian: ```json -{ "TypeDef:Struct": true, "endian": "big", "properties": { ... } } +{ "AlkType:Struct": true, "endian": "big", "properties": { ... } } ``` - `"endian": "little"` (default) — read/write in little-endian byte order. @@ -380,10 +380,10 @@ Both struct-level and field-level, with field-level overriding: ```json { - "TypeDef:Struct": true, + "AlkType:Struct": true, "align": 256, "properties": { - "weight": { "TypeDef:Float32": true, "align": 16 } + "weight": { "AlkType:Float32": true, "align": 16 } } } ``` @@ -398,23 +398,23 @@ Both struct-level and field-level, with field-level overriding: ### Variable-length encoding -The typedef engine supports three strategies for variable-length types +The alktype engine supports three strategies for variable-length types (see §Variable-length types above for full details). The strategy is selected by the `encoding` annotation and the standard JSON Schema `maxLength` keyword: ```json // Strategy 1: Inline length-prefixing (default, shorthand) -{ "TypeDef:String": true } +{ "AlkType:String": true } // Strategy 1: Explicit inline length-prefixing -{ "TypeDef:String": { "encoding": "length-prefixed" } } +{ "AlkType:String": { "encoding": "length-prefixed" } } // Strategy 2: Fixed-size reservation (uses standard maxLength) -{ "TypeDef:String": true, "maxLength": 256 } +{ "AlkType:String": true, "maxLength": 256 } // Strategy 3: Offset indirection (opt-in) -{ "TypeDef:String": { "encoding": "offset-indirect" } } +{ "AlkType:String": { "encoding": "offset-indirect" } } ``` - `"encoding": "length-prefixed"` (default) — 4-byte length prefix at @@ -428,8 +428,8 @@ selected by the `encoding` annotation and the standard JSON Schema `{offset: u32, length: u32}` pointing into a separate data region. The consumer provides the data region separately. Used by metatensor blob tensors. -- Applies to all variable-length types: `TypeDef:String`, `TypeDef:Bytes`, - `TypeDef:Array`, `TypeDef:Record`, `TypeDef:Timestamp`. +- Applies to all variable-length types: `AlkType:String`, `AlkType:Bytes`, + `AlkType:Array`, `AlkType:Record`, `AlkType:Timestamp`. ### TUnion discriminators @@ -440,11 +440,11 @@ Two discriminator kinds: byte-offset (protocol dispatch) and field-name ```json { - "TypeDef:Union": true, + "AlkType:Union": true, "discriminator": { "kind": "byte", "offset": 0, - "type": "TypeDef:Uint8" + "type": "AlkType:Uint8" }, "mapping": { "5": { "$ref": "#/$defs/Read" }, @@ -455,8 +455,8 @@ Two discriminator kinds: byte-offset (protocol dispatch) and field-name ``` - `"offset"` — byte position of the discriminator. -- `"type"` — the `TypeDef:*` kind of the discriminator (typically - `TypeDef:Uint8`). +- `"type"` — the `AlkType:*` kind of the discriminator (typically + `AlkType:Uint8`). - Mapping keys are stringified integers. The variant struct starts at `offset + discriminator_size`. @@ -464,7 +464,7 @@ Two discriminator kinds: byte-offset (protocol dispatch) and field-name ```json { - "TypeDef:Union": true, + "AlkType:Union": true, "discriminator": { "kind": "field", "name": "type" diff --git a/docs/architecture/validation.md b/docs/architecture/validation.md index e308c13..4db3419 100644 --- a/docs/architecture/validation.md +++ b/docs/architecture/validation.md @@ -5,22 +5,22 @@ last_updated: 2026-07-22 # alktype — Validation -The validation layer: custom keyword validators for all 19 `TypeDef:*` -kinds, the `TypedefError` enum, load-time vs access-time validation -strategy, and the `TypedefEngine` as the compiled form of a schema. +The validation layer: custom keyword validators for all 19 `AlkType:*` +kinds, the `AlkTypeError` enum, load-time vs access-time validation +strategy, and the `AlkTypeEngine` as the compiled form of a schema. ## Validation Strategy Validation is delegated to the `jsonschema` crate (v0.46.5, Draft -2020-12). The typedef engine does not implement its own validation — -it registers custom keyword validators for each `TypeDef:*` kind and +2020-12). The alktype engine does not implement its own validation — +it registers custom keyword validators for each `AlkType:*` kind and lets `jsonschema` handle the structural validation (object properties, required fields, array items, enum values). The strategy is decided in [ADR-004](decisions/004-error-handling-validation-strategy.md): 1. **Load time:** Parse the schema JSON, build the layout engine, build the - jsonschema validator. This is the `TypedefEngine::compile(schema)` constructor. + jsonschema validator. This is the `AlkTypeEngine::compile(schema)` constructor. 2. **Access time:** Use the compiled engine for repeated read/write operations. Validation is opt-in per operation. @@ -37,7 +37,7 @@ the correct separation of concerns: - **Binary access validation** (data access layer): the read/write functions perform type-level validation at access time — range checks for integers, UTF-8 validity for strings, buffer bounds checking. - These return `TypedefError::Access` with field paths. + These return `AlkTypeError::Access` with field paths. The "schema is the format" principle means the same schema describes both the JSON shape and the binary layout. The jsonschema validator @@ -47,13 +47,13 @@ buffer into a `Value` tree via the data access layer, then validates that `Value` against the jsonschema validator. This is a two-step process, not a single `validate(buffer)` call. -### The `TypedefEngine` struct +### The `AlkTypeEngine` struct -The `TypedefEngine` is the compiled form of a schema. It supports both +The `AlkTypeEngine` is the compiled form of a schema. It supports both layout modes (ADR-002) via an internal `Layout` enum: ```rust -pub struct TypedefEngine { +pub struct AlkTypeEngine { layout: Layout, // packed or aligned (private enum) validator: jsonschema::Validator, // compiled once at load time endian: Endian, // parsed from the schema's "endian" annotation @@ -72,8 +72,8 @@ The consumer selects the mode at construction time via `LayoutMode` enum is private — the engine exposes mode-appropriate accessors instead: ```rust -impl TypedefEngine { - pub fn compile(schema: &mut Value, mode: LayoutMode) -> Result; +impl AlkTypeEngine { + pub fn compile(schema: &mut Value, mode: LayoutMode) -> Result; pub fn mode(&self) -> LayoutMode; pub fn endian(&self) -> Endian; pub fn offset_map(&self) -> Option<&OffsetMap>; // Some in aligned mode @@ -94,39 +94,39 @@ The `SequentialReader` (read-side) is not stored — it has mutable cursor state that the consumer owns, so `sequential_reader()` constructs a fresh reader on each call (ADR-007). -The `read_field`/`write_field` methods on `TypedefEngine` are the +The `read_field`/`write_field` methods on `AlkTypeEngine` are the aligned-mode data-access API — see [data-access.md](data-access.md) §"Higher-level read/write". ## Custom Keyword Validators -Each `TypeDef:*` kind gets a `Keyword` implementation registered via +Each `AlkType:*` kind gets a `Keyword` implementation registered via `jsonschema::options().with_keyword(...)`. The validators check leaf type constraints; `jsonschema` handles all structural validation. ### Numeric type validators -**`TypeDef:Float32` / `TypeDef:Float64`:** +**`AlkType:Float32` / `AlkType:Float64`:** - Value must be a finite number. - For `Float32`: value must be representable as `f32` (no precision loss beyond `f32`'s mantissa). -**`TypeDef:Int8` / `TypeDef:Int16` / `TypeDef:Int32`:** +**`AlkType:Int8` / `AlkType:Int16` / `AlkType:Int32`:** - Value must be an integer within the type's range. - Int8: -128..127, Int16: -32768..32767, Int32: -2147483648..2147483647. -**`TypeDef:Uint8` / `TypeDef:Uint16` / `TypeDef:Uint32`:** +**`AlkType:Uint8` / `AlkType:Uint16` / `AlkType:Uint32`:** - Value must be a non-negative integer within the type's range. - Uint8: 0..255, Uint16: 0..65535, Uint32: 0..4294967295. ### String and binary validators -**`TypeDef:String`:** +**`AlkType:String`:** - Value must be a valid UTF-8 string. - If `maxLength` is specified in the schema, the string's byte length must not exceed it. -**`TypeDef:Bytes`:** +**`AlkType:Bytes`:** - Value must be a string (JSON represents binary data as a string — JSON has no native byte type). - If `maxLength` is specified, the byte length must not exceed it. @@ -135,34 +135,34 @@ type constraints; `jsonschema` handles all structural validation. validation) uses a string; the binary representation (for data access) uses `&[u8]` directly. -**`TypeDef:Enum`:** -- The `TypeDef:Enum` custom keyword signals that the type is an enum for +**`AlkType:Enum`:** +- The `AlkType:Enum` custom keyword signals that the type is an enum for *layout* purposes (the engine needs to know it's a fixed-size u32 index, not a variable-length string). The built-in `enum` keyword provides the value list and handles value-membership validation. The custom keyword validator is a no-op beyond the built-in check — it exists solely for the layout engine to recognize the type. -**`TypeDef:Timestamp`:** +**`AlkType:Timestamp`:** - Value must be a valid RFC 3339 timestamp string (the internet profile of ISO 8601, e.g., `"2026-07-20T15:30:00Z"`). ### Composite type validators -**`TypeDef:Struct`:** +**`AlkType:Struct`:** - Value must be an object. -- Each property must match its declared `TypeDef:*` kind. +- Each property must match its declared `AlkType:*` kind. - Required fields must be present. - The `jsonschema` crate's built-in `properties` and `required` keywords handle the structural checks — the custom keyword only needs to - validate that each field's value matches its `TypeDef:*` kind. + validate that each field's value matches its `AlkType:*` kind. -**`TypeDef:Union`:** +**`AlkType:Union`:** - The discriminator value must be one of the mapping keys. - The variant struct must match the declared schema for that discriminator value. -**`TypeDef:Array`:** +**`AlkType:Array`:** - Value must be an array. - Each element must match the array's declared element type. - If `minItems`/`maxItems` is specified, the array length must be within @@ -170,19 +170,19 @@ type constraints; `jsonschema` handles all structural validation. ### Other validators -**`TypeDef:Boolean`:** +**`AlkType:Boolean`:** - Value must be `true` or `false`. -**`TypeDef:Record`:** +**`AlkType:Record`:** - Value must be an object. - All values must match the record's declared value type (specified via the `"values"` property in the schema, e.g., - `"values": { "TypeDef:Float32": true }`). + `"values": { "AlkType:Float32": true }`). ### Validator implementation pattern Each custom keyword implementation is ~10 lines. Example for -`TypeDef:Float32`: +`AlkType:Float32`: ```rust struct Float32Validator; @@ -204,7 +204,7 @@ Registration: ```rust let validator = jsonschema::options() - .with_keyword("TypeDef:Float32", |parent, value, path| { + .with_keyword("AlkType:Float32", |parent, value, path| { Ok(Box::new(Float32Validator)) }) .build(&schema)?; @@ -212,18 +212,18 @@ let validator = jsonschema::options() The factory closure receives the parent schema object, the keyword's value, and the schema path. This enables cross-keyword awareness — for -example, a `TypeDef:Struct` validator can inspect the parent's -`properties` to validate each field against its declared `TypeDef:*` kind. +example, a `AlkType:Struct` validator can inspect the parent's +`properties` to validate each field against its declared `AlkType:*` kind. -## TypedefError +## AlkTypeError -A single `TypedefError` enum covers all error conditions across the +A single `AlkTypeError` enum covers all error conditions across the engine's three phases (schema parsing, offset computation, read/write) plus validation. Decided in [ADR-004](decisions/004-error-handling-validation-strategy.md). ```rust -pub enum TypedefError { - /// Schema parsing errors (invalid JSON, missing keywords, unknown TypeDef kinds). +pub enum AlkTypeError { + /// Schema parsing errors (invalid JSON, missing keywords, unknown AlkType kinds). Schema(String), /// Offset computation errors (field not found, unsupported type). Offset { field_path: String, reason: String }, @@ -234,8 +234,8 @@ pub enum TypedefError { } ``` -- **`Schema`** — for errors during `TypedefEngine::compile()`. Invalid - JSON, missing required keywords, unknown `TypeDef:*` kinds. +- **`Schema`** — for errors during `AlkTypeEngine::compile()`. Invalid + JSON, missing required keywords, unknown `AlkType:*` kinds. - **`Offset`** — for errors during offset computation. Field not found in the schema, type not supported for offset computation, recursive depth exceeded. Carries the field path. @@ -244,14 +244,14 @@ pub enum TypedefError { Carries the field path. - **`Validation`** — wraps `jsonschema`'s `ValidationError`. The `'static` lifetime is correct — the validator owns its schema reference - and lives for the lifetime of the `TypedefEngine`. + and lives for the lifetime of the `AlkTypeEngine`. ### Field-path-carrying errors Read/write and offset errors include the field path for debugging: ```rust -Err(TypedefError::Access { +Err(AlkTypeError::Access { field_path: "header.version".to_string(), reason: "buffer too short: need 4 bytes at offset 12, have 2".to_string(), }) @@ -262,7 +262,7 @@ you exactly which field failed and why. ## Validation Timing -### Load time: `TypedefEngine::compile()` +### Load time: `AlkTypeEngine::compile()` The expensive work happens once at schema load time: 1. Normalize `$ref` values in the schema (`normalize_refs`). @@ -270,7 +270,7 @@ The expensive work happens once at schema load time: 3. Compute the layout (`LayoutBuilder`/`SequentialReader` for packed, `OffsetMap` for aligned). 4. Build the jsonschema validator (`jsonschema::options().with_keyword(...).build(&schema)?`). -The result is a `TypedefEngine` that can be used for repeated operations. +The result is a `AlkTypeEngine` that can be used for repeated operations. ### Access time: `engine.validate_json(&Value)` / `engine.is_valid_json(&Value)` @@ -281,7 +281,7 @@ validator is already compiled — these are fast checks against the compiled validator. ```rust -pub fn validate_json(&self, instance: &Value) -> Result<(), TypedefError>; +pub fn validate_json(&self, instance: &Value) -> Result<(), AlkTypeError>; pub fn is_valid_json(&self, instance: &Value) -> bool; ``` @@ -314,12 +314,12 @@ representation first, then access the binary buffer. | Decision | ADR | Summary | |----------|-----|---------| -| Error handling and validation | [ADR-004](decisions/004-error-handling-validation-strategy.md) | `TypedefError` enum; load-time build, access-time check; field-path-carrying errors; jsonschema `ValidationError` wrapping | +| Error handling and validation | [ADR-004](decisions/004-error-handling-validation-strategy.md) | `AlkTypeError` enum; load-time build, access-time check; field-path-carrying errors; jsonschema `ValidationError` wrapping | | Purpose and scope | [ADR-001](decisions/001-alktype-purpose-scope-jsonschema-engine.md) | Why jsonschema not a custom engine | ## Open Questions -None specific to validation. The three typedef OQs (OQ-001, OQ-002, +None specific to validation. The three alktype OQs (OQ-001, OQ-002, OQ-003) are about layout, platform support, and schema construction — not validation. @@ -329,7 +329,7 @@ not validation. custom keyword validators for all 17 kinds - [ADR-004](decisions/004-error-handling-validation-strategy.md) — error handling and validation strategy -- [schema-layer.md](schema-layer.md) — the 17 TypeDef kinds that the +- [schema-layer.md](schema-layer.md) — the 17 AlkType kinds that the validators check - [data-access.md](data-access.md) — read/write functions that operate on the same buffers diff --git a/src/data_access.rs b/src/data_access.rs index b68a72b..b3a078f 100644 --- a/src/data_access.rs +++ b/src/data_access.rs @@ -1,22 +1,22 @@ -//! Data access layer: primitive read/write functions for all 17 TypeDef +//! Data access layer: primitive read/write functions for all 17 AlkType //! kinds with endianness support, bounds checking, and zero-copy access. //! //! These are the building blocks used by the layout types ([`crate::offset_map`], //! [`crate::layout_builder`], [`crate::sequential_reader`]) and the -//! [`crate::engine::TypedefEngine`]. Each function operates on a raw byte -//! buffer at a caller-provided offset and returns a [`TypedefError::Access`] +//! [`crate::engine::AlkTypeEngine`]. Each function operates on a raw byte +//! buffer at a caller-provided offset and returns a [`AlkTypeError::Access`] //! carrying the field path on bounds or encoding failures. //! //! # Conventions //! //! - All multi-byte types respect the [`Endian`] parameter passed by the caller. //! - Bounds checks ensure `buffer.len() >= offset + size`; failures produce -//! [`TypedefError::Access`] with a descriptive `reason`. +//! [`AlkTypeError::Access`] with a descriptive `reason`. //! - Read functions for variable-length types return slices borrowing from //! the input buffer — no allocation. //! - No `unwrap()` / `expect()` on fallible operations. -use crate::error::TypedefError; +use crate::error::AlkTypeError; use crate::schema::Endian; const U32_SIZE: usize = 4; @@ -26,9 +26,9 @@ fn check_bounds( start: usize, end: usize, field_path: &str, -) -> Result<(), TypedefError> { +) -> Result<(), AlkTypeError> { if end < start || buffer_len < end { - return Err(TypedefError::Access { + return Err(AlkTypeError::Access { field_path: field_path.to_string(), reason: format!( "buffer bounds check failed: need bytes [{start}..{end}), buffer has {buffer_len}" @@ -38,8 +38,8 @@ fn check_bounds( Ok(()) } -fn access_err(field_path: &str, reason: impl Into) -> TypedefError { - TypedefError::Access { +fn access_err(field_path: &str, reason: impl Into) -> AlkTypeError { + AlkTypeError::Access { field_path: field_path.to_string(), reason: reason.into(), } @@ -49,7 +49,7 @@ pub(crate) fn read_array( buffer: &[u8], offset: usize, field_path: &str, -) -> Result<[u8; N], TypedefError> { +) -> Result<[u8; N], AlkTypeError> { let end = offset.checked_add(N).ok_or_else(|| { access_err( field_path, @@ -79,7 +79,7 @@ pub(crate) fn write_array( offset: usize, bytes: [u8; N], field_path: &str, -) -> Result<(), TypedefError> { +) -> Result<(), AlkTypeError> { let end = offset.checked_add(N).ok_or_else(|| { access_err( field_path, @@ -129,9 +129,9 @@ define_read_write_endian!(f64, read_f64, write_f64, 8); /// Read a `bool` at `offset` from `buffer`. /// /// `0x00` decodes to `false`, `0x01` decodes to `true`. Any other byte value -/// produces [`TypedefError::Access`] with a reason of the form +/// produces [`AlkTypeError::Access`] with a reason of the form /// `"invalid boolean byte 0x02 at offset {offset}"`. -pub fn read_bool(buffer: &[u8], offset: usize, field_path: &str) -> Result { +pub fn read_bool(buffer: &[u8], offset: usize, field_path: &str) -> Result { let bytes: [u8; 1] = read_array(buffer, offset, field_path)?; match bytes[0] { 0x00 => Ok(false), @@ -151,7 +151,7 @@ pub fn write_bool( offset: usize, value: bool, field_path: &str, -) -> Result<(), TypedefError> { +) -> Result<(), AlkTypeError> { write_array( buffer, offset, @@ -168,7 +168,7 @@ pub fn read_enum( offset: usize, field_path: &str, endian: Endian, -) -> Result { +) -> Result { read_u32(buffer, offset, field_path, endian) } @@ -179,7 +179,7 @@ pub fn write_enum( value: u32, field_path: &str, endian: Endian, -) -> Result<(), TypedefError> { +) -> Result<(), AlkTypeError> { write_u32(buffer, offset, value, field_path, endian) } @@ -191,13 +191,13 @@ pub fn write_enum( /// /// Wire format: `[length: u32][UTF-8 bytes]`. The length prefix respects /// `endian`. Returns a `&'a str` that borrows from the input buffer — no -/// allocation. Invalid UTF-8 produces [`TypedefError::Access`]. +/// allocation. Invalid UTF-8 produces [`AlkTypeError::Access`]. pub fn read_string<'a>( buffer: &'a [u8], offset: usize, field_path: &str, endian: Endian, -) -> Result<&'a str, TypedefError> { +) -> Result<&'a str, AlkTypeError> { let bytes = read_bytes(buffer, offset, field_path, endian)?; std::str::from_utf8(bytes).map_err(|e| { access_err( @@ -216,7 +216,7 @@ pub fn read_bytes<'a>( offset: usize, field_path: &str, endian: Endian, -) -> Result<&'a [u8], TypedefError> { +) -> Result<&'a [u8], AlkTypeError> { let len_bytes: [u8; U32_SIZE] = read_array(buffer, offset, field_path)?; let len = u32_from(len_bytes, endian) as usize; let data_start = offset.checked_add(U32_SIZE).ok_or_else(|| { @@ -246,7 +246,7 @@ pub fn write_string( value: &str, field_path: &str, endian: Endian, -) -> Result { +) -> Result { write_bytes(buffer, offset, value.as_bytes(), field_path, endian) } @@ -260,7 +260,7 @@ pub fn write_bytes( value: &[u8], field_path: &str, endian: Endian, -) -> Result { +) -> Result { let data_len = value.len(); let total = U32_SIZE.checked_add(data_len).ok_or_else(|| { access_err( @@ -297,14 +297,14 @@ pub fn write_bytes( /// `{ data_offset: u32, data_length: u32 }` (endian-aware). The actual UTF-8 /// bytes live in `data_region[data_offset..data_offset+data_length]`. Returns /// a `&'a str` borrowing from `data_region`. Invalid UTF-8 produces -/// [`TypedefError::Access`]. +/// [`AlkTypeError::Access`]. pub fn read_string_indirect<'a>( buffer: &'a [u8], offset: usize, data_region: &'a [u8], field_path: &str, endian: Endian, -) -> Result<&'a str, TypedefError> { +) -> Result<&'a str, AlkTypeError> { let bytes = read_bytes_indirect(buffer, offset, data_region, field_path, endian)?; std::str::from_utf8(bytes).map_err(|e| { access_err( @@ -325,7 +325,7 @@ pub fn read_bytes_indirect<'a>( data_region: &'a [u8], field_path: &str, endian: Endian, -) -> Result<&'a [u8], TypedefError> { +) -> Result<&'a [u8], AlkTypeError> { let struct_end = offset .checked_add(8) .ok_or_else(|| access_err(field_path, format!("offset {offset} + 8 overflows usize")))?; @@ -476,7 +476,7 @@ mod tests { let buf = [0x02u8]; let err = read_bool(&buf, 0, "f").unwrap_err(); match err { - TypedefError::Access { field_path, reason } => { + AlkTypeError::Access { field_path, reason } => { assert_eq!(field_path, "f"); assert!(reason.contains("0x02"), "reason: {reason}"); assert!(reason.contains("offset 0"), "reason: {reason}"); @@ -500,7 +500,7 @@ mod tests { let buf = [0u8; 2]; let err = read_u32(&buf, 0, "header.id", LE).unwrap_err(); match err { - TypedefError::Access { field_path, reason } => { + AlkTypeError::Access { field_path, reason } => { assert_eq!(field_path, "header.id"); assert!(reason.contains("bounds"), "reason: {reason}"); } @@ -512,7 +512,7 @@ mod tests { fn write_bounds_failure_returns_access_error() { let mut buf = [0u8; 2]; let err = write_u32(&mut buf, 0, 1, "header.id", LE).unwrap_err(); - assert!(matches!(err, TypedefError::Access { .. })); + assert!(matches!(err, AlkTypeError::Access { .. })); } #[test] @@ -548,14 +548,14 @@ mod tests { let mut buf = vec![0u8; 16]; write_bytes(&mut buf, 0, &[0xFF, 0xFE, 0xFD], "name", LE).unwrap(); let err = read_string(&buf, 0, "name", LE).unwrap_err(); - assert!(matches!(err, TypedefError::Access { .. })); + assert!(matches!(err, AlkTypeError::Access { .. })); } #[test] fn read_string_bounds_failure_on_prefix() { let buf = [0u8; 2]; let err = read_string(&buf, 0, "name", LE).unwrap_err(); - assert!(matches!(err, TypedefError::Access { .. })); + assert!(matches!(err, AlkTypeError::Access { .. })); } #[test] @@ -565,14 +565,14 @@ mod tests { let len_bytes = (100u32).to_le_bytes(); buf[0..4].copy_from_slice(&len_bytes); let err = read_string(&buf, 0, "name", LE).unwrap_err(); - assert!(matches!(err, TypedefError::Access { .. })); + assert!(matches!(err, AlkTypeError::Access { .. })); } #[test] fn write_string_bounds_failure() { let mut buf = vec![0u8; 4]; let err = write_string(&mut buf, 0, "hello", "name", LE).unwrap_err(); - assert!(matches!(err, TypedefError::Access { .. })); + assert!(matches!(err, AlkTypeError::Access { .. })); } #[test] @@ -600,7 +600,7 @@ mod tests { let buf = [0u8; 4]; let data_region = b"anything"; let err = read_bytes_indirect(&buf, 0, data_region, "blob", LE).unwrap_err(); - assert!(matches!(err, TypedefError::Access { .. })); + assert!(matches!(err, AlkTypeError::Access { .. })); } #[test] @@ -610,7 +610,7 @@ mod tests { write_u32(&mut buf, 4, 10, "idx.len", LE).unwrap(); let data_region = b"too short"; let err = read_bytes_indirect(&buf, 0, data_region, "blob", LE).unwrap_err(); - assert!(matches!(err, TypedefError::Access { .. })); + assert!(matches!(err, AlkTypeError::Access { .. })); } #[test] diff --git a/src/engine.rs b/src/engine.rs index 2abed79..1f2763a 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1,19 +1,19 @@ -//! `TypedefEngine` — the compiled form of a schema. +//! `AlkTypeEngine` — the compiled form of a schema. //! //! Combines the layout engine (both packed and aligned modes) and the //! jsonschema validator into a single struct. Built once at schema load -//! time via [`TypedefEngine::compile`]. Used for repeated read/write/ +//! time via [`AlkTypeEngine::compile`]. Used for repeated read/write/ //! validate operations at access time. //! -//! See [validation.md](../../docs/architecture/crates/typedef/validation.md) -//! §"The TypedefEngine struct" and -//! [overview.md](../../docs/architecture/crates/typedef/overview.md). +//! See [validation.md](../../docs/architecture/validation.md) +//! §"The AlkTypeEngine struct" and +//! [overview.md](../../docs/architecture/overview.md). use crate::data_access; -use crate::error::TypedefError; +use crate::error::AlkTypeError; use crate::layout_builder::LayoutBuilder; use crate::offset_map::OffsetMap; -use crate::schema::{self, get_typedef_kind_loose_enum, Endian, TypeDefKind}; +use crate::schema::{self, get_alktype_kind_loose_enum, Endian, AlkTypeKind}; use crate::sequential_reader::{FieldValue, SequentialReader}; use crate::validation; use serde_json::Value; @@ -32,7 +32,7 @@ pub enum LayoutMode { /// /// Carries the layout-specific handles needed for read/write access in /// the selected mode. The consumer chooses the mode at construction time -/// via [`TypedefEngine::compile`]; the engine then exposes only the +/// via [`AlkTypeEngine::compile`]; the engine then exposes only the /// APIs that make sense for that mode. #[derive(Debug)] enum Layout { @@ -48,26 +48,26 @@ enum Layout { Aligned { offset_map: OffsetMap }, } -/// The compiled form of a typedef schema. Combines the layout engine +/// The compiled form of a alktype schema. Combines the layout engine /// (both packed and aligned modes) and the jsonschema validator. /// -/// Built once at schema load time via [`TypedefEngine::compile`]. +/// Built once at schema load time via [`AlkTypeEngine::compile`]. /// Used for repeated read/write/validate operations at access time. /// /// The consumer selects the layout mode at construction time. The engine -/// then exposes mode-appropriate accessors: [`TypedefEngine::offset_map`] -/// for aligned mode, [`TypedefEngine::layout_builder`] and -/// [`TypedefEngine::sequential_reader`] for packed mode. The +/// then exposes mode-appropriate accessors: [`AlkTypeEngine::offset_map`] +/// for aligned mode, [`AlkTypeEngine::layout_builder`] and +/// [`AlkTypeEngine::sequential_reader`] for packed mode. The /// jsonschema validator is mode-agnostic and always available. -pub struct TypedefEngine { +pub struct AlkTypeEngine { layout: Layout, validator: jsonschema::Validator, endian: Endian, schema: Value, } -impl TypedefEngine { - /// Compile a schema into a [`TypedefEngine`]. +impl AlkTypeEngine { + /// Compile a schema into a [`AlkTypeEngine`]. /// /// This is the expensive operation — it parses the schema, normalizes /// `$ref` values, computes the layout, and builds the jsonschema @@ -79,11 +79,11 @@ impl TypedefEngine { /// /// # Errors /// - /// Returns [`TypedefError::Schema`] if the schema is malformed or the + /// Returns [`AlkTypeError::Schema`] if the schema is malformed or the /// underlying layout/validator construction fails. The error is /// propagated from [`LayoutBuilder::new`], [`SequentialReader::new`], /// [`OffsetMap::compute`], or [`validation::build_validator`]. - pub fn compile(schema: &mut Value, mode: LayoutMode) -> Result { + pub fn compile(schema: &mut Value, mode: LayoutMode) -> Result { schema::normalize_refs(schema); let endian = Endian::from_schema(schema); let layout = match mode { @@ -152,12 +152,12 @@ impl TypedefEngine { /// Validate a JSON value against the schema. The jsonschema validator /// is already compiled — this is a fast check. /// - /// Returns `Ok(())` if valid, `Err(TypedefError::Validation(...))` if + /// Returns `Ok(())` if valid, `Err(AlkTypeError::Validation(...))` if /// invalid. - pub fn validate_json(&self, instance: &Value) -> Result<(), TypedefError> { + pub fn validate_json(&self, instance: &Value) -> Result<(), AlkTypeError> { self.validator .validate(instance) - .map_err(|e| TypedefError::Validation(e.to_owned())) + .map_err(|e| AlkTypeError::Validation(e.to_owned())) } /// Check if a JSON value is valid against the schema. @@ -173,25 +173,25 @@ impl TypedefEngine { /// `Bytes`/`Timestamp` fields. /// /// Returns an error if compiled in packed mode — use - /// [`TypedefEngine::sequential_reader`] for packed mode. Also + /// [`AlkTypeEngine::sequential_reader`] for packed mode. Also /// returns an error for composite kinds (`Struct`, `Union`, `Array`, /// `Record`) — those are better handled via the layout-specific APIs. /// /// # Errors /// - /// - [`TypedefError::Access`] if compiled in packed mode. - /// - [`TypedefError::Offset`] if `field_path` is not in the offset map. - /// - [`TypedefError::Access`] for buffer-too-short or invalid data, + /// - [`AlkTypeError::Access`] if compiled in packed mode. + /// - [`AlkTypeError::Offset`] if `field_path` is not in the offset map. + /// - [`AlkTypeError::Access`] for buffer-too-short or invalid data, /// propagated from [`crate::data_access`]. pub fn read_field<'a>( &self, buffer: &'a [u8], field_path: &str, - ) -> Result, TypedefError> { + ) -> Result, AlkTypeError> { let offset_map = match &self.layout { Layout::Aligned { offset_map } => offset_map, Layout::Packed { .. } => { - return Err(TypedefError::Access { + return Err(AlkTypeError::Access { field_path: field_path.to_string(), reason: "read_field is only available in aligned mode; \ use sequential_reader() for packed mode" @@ -201,87 +201,87 @@ impl TypedefEngine { }; let range = offset_map .get(field_path) - .ok_or_else(|| TypedefError::Offset { + .ok_or_else(|| AlkTypeError::Offset { field_path: field_path.to_string(), reason: "field not found in offset map".to_string(), })?; let field_schema = - lookup_field_schema(&self.schema, field_path).ok_or_else(|| TypedefError::Offset { + lookup_field_schema(&self.schema, field_path).ok_or_else(|| AlkTypeError::Offset { field_path: field_path.to_string(), reason: "field schema not found in schema tree".to_string(), })?; - let kind = get_typedef_kind_loose_enum(field_schema).ok_or_else(|| TypedefError::Offset { + let kind = get_alktype_kind_loose_enum(field_schema).ok_or_else(|| AlkTypeError::Offset { field_path: field_path.to_string(), - reason: "field schema has no TypeDef:* kind".to_string(), + reason: "field schema has no AlkType:* kind".to_string(), })?; let endian = self.endian; match kind { - TypeDefKind::Int8 => { + AlkTypeKind::Int8 => { let v = data_access::read_i8(buffer, range.start, field_path)?; Ok(FieldValue::I8(v)) } - TypeDefKind::Int16 => { + AlkTypeKind::Int16 => { let v = data_access::read_i16(buffer, range.start, field_path, endian)?; Ok(FieldValue::I16(v)) } - TypeDefKind::Int32 => { + AlkTypeKind::Int32 => { let v = data_access::read_i32(buffer, range.start, field_path, endian)?; Ok(FieldValue::I32(v)) } - TypeDefKind::Int64 => { + AlkTypeKind::Int64 => { let v = data_access::read_i64(buffer, range.start, field_path, endian)?; Ok(FieldValue::I64(v)) } - TypeDefKind::Uint8 => { + AlkTypeKind::Uint8 => { let v = data_access::read_u8(buffer, range.start, field_path)?; Ok(FieldValue::U8(v)) } - TypeDefKind::Uint16 => { + AlkTypeKind::Uint16 => { let v = data_access::read_u16(buffer, range.start, field_path, endian)?; Ok(FieldValue::U16(v)) } - TypeDefKind::Uint32 => { + AlkTypeKind::Uint32 => { let v = data_access::read_u32(buffer, range.start, field_path, endian)?; Ok(FieldValue::U32(v)) } - TypeDefKind::Uint64 => { + AlkTypeKind::Uint64 => { let v = data_access::read_u64(buffer, range.start, field_path, endian)?; Ok(FieldValue::U64(v)) } - TypeDefKind::Float32 => { + AlkTypeKind::Float32 => { let v = data_access::read_f32(buffer, range.start, field_path, endian)?; Ok(FieldValue::F32(v)) } - TypeDefKind::Float64 => { + AlkTypeKind::Float64 => { let v = data_access::read_f64(buffer, range.start, field_path, endian)?; Ok(FieldValue::F64(v)) } - TypeDefKind::Boolean => { + AlkTypeKind::Boolean => { let v = data_access::read_bool(buffer, range.start, field_path)?; Ok(FieldValue::Bool(v)) } - TypeDefKind::Enum => { + AlkTypeKind::Enum => { let v = data_access::read_enum(buffer, range.start, field_path, endian)?; Ok(FieldValue::Enum(v)) } - TypeDefKind::String => { + AlkTypeKind::String => { let v = data_access::read_string(buffer, range.start, field_path, endian)?; Ok(FieldValue::String(v)) } - TypeDefKind::Bytes => { + AlkTypeKind::Bytes => { let v = data_access::read_bytes(buffer, range.start, field_path, endian)?; Ok(FieldValue::Bytes(v)) } - TypeDefKind::Timestamp => { + AlkTypeKind::Timestamp => { let v = data_access::read_string(buffer, range.start, field_path, endian)?; Ok(FieldValue::String(v)) } - TypeDefKind::Struct => Ok(FieldValue::Struct { + AlkTypeKind::Struct => Ok(FieldValue::Struct { start: range.start, end: range.end, }), - TypeDefKind::Union | TypeDefKind::Array | TypeDefKind::Record => { - Err(TypedefError::Access { + AlkTypeKind::Union | AlkTypeKind::Array | AlkTypeKind::Record => { + Err(AlkTypeError::Access { field_path: field_path.to_string(), reason: "read_field does not support composite types; \ use the layout-specific APIs" @@ -299,25 +299,25 @@ impl TypedefEngine { /// `Bytes`/`Timestamp` fields. /// /// Returns an error if compiled in packed mode — use - /// [`TypedefEngine::layout_builder`] for packed mode. Also returns an + /// [`AlkTypeEngine::layout_builder`] for packed mode. Also returns an /// error for composite kinds (`Struct`, `Union`, `Array`, `Record`). /// /// # Errors /// - /// - [`TypedefError::Access`] if compiled in packed mode. - /// - [`TypedefError::Offset`] if `field_path` is not in the offset map. - /// - [`TypedefError::Access`] for buffer-too-short or invalid data, + /// - [`AlkTypeError::Access`] if compiled in packed mode. + /// - [`AlkTypeError::Offset`] if `field_path` is not in the offset map. + /// - [`AlkTypeError::Access`] for buffer-too-short or invalid data, /// propagated from [`crate::data_access`]. pub fn write_field( &self, buffer: &mut [u8], field_path: &str, value: &FieldValue<'_>, - ) -> Result<(), TypedefError> { + ) -> Result<(), AlkTypeError> { let offset_map = match &self.layout { Layout::Aligned { offset_map } => offset_map, Layout::Packed { .. } => { - return Err(TypedefError::Access { + return Err(AlkTypeError::Access { field_path: field_path.to_string(), reason: "write_field is only available in aligned mode; \ use layout_builder() for packed mode" @@ -327,7 +327,7 @@ impl TypedefEngine { }; let range = offset_map .get(field_path) - .ok_or_else(|| TypedefError::Offset { + .ok_or_else(|| AlkTypeError::Offset { field_path: field_path.to_string(), reason: "field not found in offset map".to_string(), })?; @@ -372,7 +372,7 @@ impl TypedefEngine { Ok(()) } FieldValue::Struct { .. } | FieldValue::Union { .. } | FieldValue::Array { .. } => { - Err(TypedefError::Access { + Err(AlkTypeError::Access { field_path: field_path.to_string(), reason: "write_field does not support composite types; \ use the layout-specific APIs" @@ -383,9 +383,9 @@ impl TypedefEngine { } } -impl fmt::Debug for TypedefEngine { +impl fmt::Debug for AlkTypeEngine { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("TypedefEngine") + f.debug_struct("AlkTypeEngine") .field("layout", &self.layout) .field("validator", &"") .field("endian", &self.endian) @@ -416,13 +416,13 @@ mod tests { fn fixed_struct_schema() -> Value { json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "endian": "little", "properties": { - "flag": { "TypeDef:Uint8": true }, - "id": { "TypeDef:Uint32": true }, - "score": { "TypeDef:Float32": true }, - "tag": { "TypeDef:String": true } + "flag": { "AlkType:Uint8": true }, + "id": { "AlkType:Uint32": true }, + "score": { "AlkType:Float32": true }, + "tag": { "AlkType:String": true } } }) } @@ -430,7 +430,7 @@ mod tests { #[test] fn compile_aligned_builds_offset_map() { let mut schema = fixed_struct_schema(); - let engine = TypedefEngine::compile(&mut schema, LayoutMode::Aligned).expect("compile"); + let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Aligned).expect("compile"); assert_eq!(engine.mode(), LayoutMode::Aligned); assert!(engine.offset_map().is_some()); assert!(engine.layout_builder().is_none()); @@ -440,7 +440,7 @@ mod tests { #[test] fn compile_packed_builds_builder_and_reader() { let mut schema = fixed_struct_schema(); - let engine = TypedefEngine::compile(&mut schema, LayoutMode::Packed).expect("compile"); + let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Packed).expect("compile"); assert_eq!(engine.mode(), LayoutMode::Packed); assert!(engine.layout_builder().is_some()); assert!(engine.sequential_reader().is_some()); @@ -450,18 +450,18 @@ mod tests { #[test] fn compile_normalizes_refs() { let mut schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { "child": { "$ref": "Child" } }, "$defs": { "Child": { - "TypeDef:Struct": true, - "properties": { "x": { "TypeDef:Uint8": true } } + "AlkType:Struct": true, + "properties": { "x": { "AlkType:Uint8": true } } } } }); - let engine = TypedefEngine::compile(&mut schema, LayoutMode::Packed).expect("compile"); + let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Packed).expect("compile"); assert_eq!( engine.schema["properties"]["child"]["$ref"], json!("#/$defs/Child") @@ -471,64 +471,64 @@ mod tests { #[test] fn endian_parsed_from_schema() { let mut schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "endian": "big", - "properties": { "id": { "TypeDef:Uint32": true } } + "properties": { "id": { "AlkType:Uint32": true } } }); - let engine = TypedefEngine::compile(&mut schema, LayoutMode::Packed).expect("compile"); + let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Packed).expect("compile"); assert_eq!(engine.endian(), Endian::Big); } #[test] fn endian_defaults_to_little() { let mut schema = json!({ - "TypeDef:Struct": true, - "properties": { "id": { "TypeDef:Uint32": true } } + "AlkType:Struct": true, + "properties": { "id": { "AlkType:Uint32": true } } }); - let engine = TypedefEngine::compile(&mut schema, LayoutMode::Packed).expect("compile"); + let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Packed).expect("compile"); assert_eq!(engine.endian(), Endian::Little); } #[test] fn validate_json_accepts_valid_instance() { let mut schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "type": "object", "properties": { - "id": { "TypeDef:Uint32": true, "type": "integer" } + "id": { "AlkType:Uint32": true, "type": "integer" } }, "required": ["id"] }); - let engine = TypedefEngine::compile(&mut schema, LayoutMode::Aligned).expect("compile"); + let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Aligned).expect("compile"); assert!(engine.validate_json(&json!({"id": 42})).is_ok()); } #[test] fn validate_json_rejects_invalid_instance() { let mut schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "type": "object", "properties": { - "id": { "TypeDef:Uint32": true, "type": "integer" } + "id": { "AlkType:Uint32": true, "type": "integer" } }, "required": ["id"] }); - let engine = TypedefEngine::compile(&mut schema, LayoutMode::Aligned).expect("compile"); + let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Aligned).expect("compile"); let err = engine.validate_json(&json!({"id": -1})).unwrap_err(); - assert!(matches!(err, TypedefError::Validation(_)), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Validation(_)), "got {err:?}"); } #[test] fn is_valid_json_returns_bool() { let mut schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "type": "object", "properties": { - "id": { "TypeDef:Uint32": true, "type": "integer" } + "id": { "AlkType:Uint32": true, "type": "integer" } }, "required": ["id"] }); - let engine = TypedefEngine::compile(&mut schema, LayoutMode::Aligned).expect("compile"); + let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Aligned).expect("compile"); assert!(engine.is_valid_json(&json!({"id": 42}))); assert!(!engine.is_valid_json(&json!({"id": -1}))); } @@ -536,14 +536,14 @@ mod tests { #[test] fn read_field_aligned_reads_fixed_fields() { let mut schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "endian": "little", "properties": { - "flag": { "TypeDef:Uint8": true }, - "id": { "TypeDef:Uint32": true } + "flag": { "AlkType:Uint8": true }, + "id": { "AlkType:Uint32": true } } }); - let engine = TypedefEngine::compile(&mut schema, LayoutMode::Aligned).expect("compile"); + let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Aligned).expect("compile"); let mut buf = vec![0u8; 8]; buf[0] = 0xAB; buf[4..8].copy_from_slice(&0x01020304u32.to_le_bytes()); @@ -560,12 +560,12 @@ mod tests { #[test] fn read_field_aligned_reads_string_length_prefixed() { let mut schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "name": { "TypeDef:String": true } + "name": { "AlkType:String": true } } }); - let engine = TypedefEngine::compile(&mut schema, LayoutMode::Aligned).expect("compile"); + let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Aligned).expect("compile"); let mut buf = vec![0u8; 32]; let len_bytes = 5u32.to_le_bytes(); buf[0..4].copy_from_slice(&len_bytes); @@ -579,55 +579,55 @@ mod tests { #[test] fn read_field_returns_access_error_in_packed_mode() { let mut schema = json!({ - "TypeDef:Struct": true, - "properties": { "id": { "TypeDef:Uint32": true } } + "AlkType:Struct": true, + "properties": { "id": { "AlkType:Uint32": true } } }); - let engine = TypedefEngine::compile(&mut schema, LayoutMode::Packed).expect("compile"); + let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Packed).expect("compile"); let buf = [0u8; 4]; let err = engine.read_field(&buf, "id").unwrap_err(); - assert!(matches!(err, TypedefError::Access { .. }), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Access { .. }), "got {err:?}"); } #[test] fn read_field_returns_offset_error_for_missing_field() { let mut schema = json!({ - "TypeDef:Struct": true, - "properties": { "id": { "TypeDef:Uint32": true } } + "AlkType:Struct": true, + "properties": { "id": { "AlkType:Uint32": true } } }); - let engine = TypedefEngine::compile(&mut schema, LayoutMode::Aligned).expect("compile"); + let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Aligned).expect("compile"); let buf = [0u8; 4]; let err = engine.read_field(&buf, "missing").unwrap_err(); - assert!(matches!(err, TypedefError::Offset { .. }), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Offset { .. }), "got {err:?}"); } #[test] fn read_field_returns_error_for_composite_types() { let mut schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { "vals": { - "TypeDef:Array": true, - "items": { "TypeDef:Uint32": true } + "AlkType:Array": true, + "items": { "AlkType:Uint32": true } } } }); - let engine = TypedefEngine::compile(&mut schema, LayoutMode::Aligned).expect("compile"); + let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Aligned).expect("compile"); let buf = [0u8; 8]; let err = engine.read_field(&buf, "vals").unwrap_err(); - assert!(matches!(err, TypedefError::Access { .. }), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Access { .. }), "got {err:?}"); } #[test] fn write_field_aligned_writes_fixed_fields() { let mut schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "endian": "little", "properties": { - "flag": { "TypeDef:Uint8": true }, - "id": { "TypeDef:Uint32": true } + "flag": { "AlkType:Uint8": true }, + "id": { "AlkType:Uint32": true } } }); - let engine = TypedefEngine::compile(&mut schema, LayoutMode::Aligned).expect("compile"); + let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Aligned).expect("compile"); let mut buf = vec![0u8; 8]; engine .write_field(&mut buf, "flag", &FieldValue::U8(0xAB)) @@ -650,12 +650,12 @@ mod tests { #[test] fn write_field_round_trips_string() { let mut schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "name": { "TypeDef:String": true } + "name": { "AlkType:String": true } } }); - let engine = TypedefEngine::compile(&mut schema, LayoutMode::Aligned).expect("compile"); + let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Aligned).expect("compile"); let mut buf = vec![0u8; 32]; engine .write_field(&mut buf, "name", &FieldValue::String("hello")) @@ -669,86 +669,86 @@ mod tests { #[test] fn write_field_returns_access_error_in_packed_mode() { let mut schema = json!({ - "TypeDef:Struct": true, - "properties": { "id": { "TypeDef:Uint32": true } } + "AlkType:Struct": true, + "properties": { "id": { "AlkType:Uint32": true } } }); - let engine = TypedefEngine::compile(&mut schema, LayoutMode::Packed).expect("compile"); + let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Packed).expect("compile"); let mut buf = [0u8; 4]; let err = engine .write_field(&mut buf, "id", &FieldValue::U32(1)) .unwrap_err(); - assert!(matches!(err, TypedefError::Access { .. }), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Access { .. }), "got {err:?}"); } #[test] fn write_field_returns_offset_error_for_missing_field() { let mut schema = json!({ - "TypeDef:Struct": true, - "properties": { "id": { "TypeDef:Uint32": true } } + "AlkType:Struct": true, + "properties": { "id": { "AlkType:Uint32": true } } }); - let engine = TypedefEngine::compile(&mut schema, LayoutMode::Aligned).expect("compile"); + let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Aligned).expect("compile"); let mut buf = [0u8; 4]; let err = engine .write_field(&mut buf, "missing", &FieldValue::U32(1)) .unwrap_err(); - assert!(matches!(err, TypedefError::Offset { .. }), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Offset { .. }), "got {err:?}"); } #[test] fn write_field_returns_error_for_composite_value() { let mut schema = json!({ - "TypeDef:Struct": true, - "properties": { "id": { "TypeDef:Uint32": true } } + "AlkType:Struct": true, + "properties": { "id": { "AlkType:Uint32": true } } }); - let engine = TypedefEngine::compile(&mut schema, LayoutMode::Aligned).expect("compile"); + let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Aligned).expect("compile"); let mut buf = [0u8; 8]; let err = engine .write_field(&mut buf, "id", &FieldValue::Struct { start: 0, end: 4 }) .unwrap_err(); - assert!(matches!(err, TypedefError::Access { .. }), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Access { .. }), "got {err:?}"); } #[test] fn compile_returns_schema_error_for_invalid_top_level() { let mut schema = json!({ "type": "object", "properties": {} }); - let err = TypedefEngine::compile(&mut schema, LayoutMode::Packed).unwrap_err(); - assert!(matches!(err, TypedefError::Schema(_)), "got {err:?}"); + let err = AlkTypeEngine::compile(&mut schema, LayoutMode::Packed).unwrap_err(); + assert!(matches!(err, AlkTypeError::Schema(_)), "got {err:?}"); } #[test] fn debug_formats_without_panicking() { let mut schema = fixed_struct_schema(); - let engine = TypedefEngine::compile(&mut schema, LayoutMode::Aligned).expect("compile"); + let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Aligned).expect("compile"); let s = format!("{engine:?}"); - assert!(s.contains("TypedefEngine")); + assert!(s.contains("AlkTypeEngine")); assert!(s.contains("Aligned")); } #[test] fn lookup_field_schema_walks_dotted_path() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { "header": { - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "version": { "TypeDef:Uint8": true } + "version": { "AlkType:Uint8": true } } } } }); let node = lookup_field_schema(&schema, "header.version").expect("found"); - assert_eq!(node, &json!({ "TypeDef:Uint8": true })); + assert_eq!(node, &json!({ "AlkType:Uint8": true })); assert!(lookup_field_schema(&schema, "header.missing").is_none()); assert!(lookup_field_schema(&schema, "missing").is_none()); } #[test] - fn typedef_kind_loose_recognizes_object_form() { - let node = json!({ "TypeDef:String": { "encoding": "offset-indirect" } }); + fn alktype_kind_loose_recognizes_object_form() { + let node = json!({ "AlkType:String": { "encoding": "offset-indirect" } }); assert_eq!( - get_typedef_kind_loose_enum(&node), - Some(TypeDefKind::String) + get_alktype_kind_loose_enum(&node), + Some(AlkTypeKind::String) ); } } diff --git a/src/error.rs b/src/error.rs index 113419a..60f568a 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,16 +1,16 @@ -//! Error types for the typedef engine. +//! Error types for the alktype engine. //! -//! Decided in ADR-098: a single `TypedefError` enum covers all error +//! Decided in ADR-098: a single `AlkTypeError` enum covers all error //! conditions across the engine's three phases (schema parsing, offset //! computation, read/write) plus validation. use std::fmt; -/// Errors produced by the typedef engine across all phases. +/// Errors produced by the alktype engine across all phases. #[derive(Debug)] -pub enum TypedefError { +pub enum AlkTypeError { /// Schema parsing errors — invalid JSON, missing required keywords, - /// unknown `TypeDef:*` kinds, malformed annotations. + /// unknown `AlkType:*` kinds, malformed annotations. Schema(String), /// Offset computation errors — field not found, type not supported @@ -23,23 +23,23 @@ pub enum TypedefError { /// Validation errors — delegated to the `jsonschema` crate. /// The `'static` lifetime is correct: the validator owns its schema - /// reference and lives for the lifetime of the `TypedefEngine`. + /// reference and lives for the lifetime of the `AlkTypeEngine`. Validation(jsonschema::ValidationError<'static>), } -impl fmt::Display for TypedefError { +impl fmt::Display for AlkTypeError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - TypedefError::Schema(msg) => write!(f, "schema error: {msg}"), - TypedefError::Offset { field_path, reason } => { + AlkTypeError::Schema(msg) => write!(f, "schema error: {msg}"), + AlkTypeError::Offset { field_path, reason } => { write!(f, "offset error at {field_path}: {reason}") } - TypedefError::Access { field_path, reason } => { + AlkTypeError::Access { field_path, reason } => { write!(f, "access error at {field_path}: {reason}") } - TypedefError::Validation(inner) => write!(f, "validation error: {inner}"), + AlkTypeError::Validation(inner) => write!(f, "validation error: {inner}"), } } } -impl std::error::Error for TypedefError {} +impl std::error::Error for AlkTypeError {} diff --git a/src/layout_builder.rs b/src/layout_builder.rs index efbc6a8..ab95b58 100644 --- a/src/layout_builder.rs +++ b/src/layout_builder.rs @@ -6,7 +6,7 @@ //! each field. Used at write time when the consumer knows the data sizes //! upfront. //! -//! Per [layout-engine.md](../../docs/architecture/crates/typedef/layout-engine.md) +//! Per [layout-engine.md](../../docs/architecture/layout-engine.md) //! §"Mode 1: Packed sequential". //! //! # Layout rules @@ -15,38 +15,38 @@ //! known byte size; the offset advances by the size. No alignment //! padding is inserted (the `u32` at offset 1 is unaligned — correct //! for protocol wire formats). -//! - **Variable-length fields** (`TypeDef:String`, `TypeDef:Bytes`, -//! `TypeDef:Timestamp`, `TypeDef:Record`): always inline +//! - **Variable-length fields** (`AlkType:String`, `AlkType:Bytes`, +//! `AlkType:Timestamp`, `AlkType:Record`): always inline //! length-prefixed in packed mode. The 4-byte length prefix is //! recorded at the current offset; the offset advances by `4 + //! data_size` where `data_size` comes from `var_sizes` keyed by the //! field's dotted path. -//! - **`TypeDef:Struct`**: recurses into `properties`, propagating the +//! - **`AlkType:Struct`**: recurses into `properties`, propagating the //! field path prefix (e.g., `"header.version"`). No padding before, //! between, or after the struct's fields. -//! - **`TypeDef:Array`** of fixed-size elements with a fixed count +//! - **`AlkType:Array`** of fixed-size elements with a fixed count //! (`minItems == maxItems`): element `i` at //! `array_offset + i × element_size`. Each element is recorded as //! `"[i]"`. -//! - **`TypeDef:Array`** with a variable count: a 4-byte count prefix +//! - **`AlkType:Array`** with a variable count: a 4-byte count prefix //! at the array's offset. The consumer provides the total element //! data size in `var_sizes` under the array's field path; the builder //! adds `4 + data_size`. -//! - **`TypeDef:Union`** with a byte-offset discriminator: the +//! - **`AlkType:Union`** with a byte-offset discriminator: the //! discriminator is recorded at `".__discriminator"`. //! The consumer provides the discriminator value as a `usize` in //! `var_sizes` under `".__discriminator"`. The variant //! struct is laid out starting at `union_offset + disc_offset + //! disc_size`, with field paths prefixed by the union's path. -//! - **`TypeDef:Union`** with a field-name discriminator: the consumer +//! - **`AlkType:Union`** with a field-name discriminator: the consumer //! provides the 0-based variant index in `var_sizes` under //! `".__variant"`. The selected variant struct is laid //! out at the union's offset, with field paths prefixed by the //! union's path. The discriminator field is a regular field within //! the variant struct. -use crate::error::TypedefError; -use crate::schema::{self, get_typedef_kind_loose_enum, resolve_ref_or_inline, DiscriminatorKind, Endian, TypeDefKind, DISCRIMINATOR_PATH, U32_SIZE}; +use crate::error::AlkTypeError; +use crate::schema::{self, get_alktype_kind_loose_enum, resolve_ref_or_inline, DiscriminatorKind, Endian, AlkTypeKind, DISCRIMINATOR_PATH, U32_SIZE}; use serde_json::Value; use std::collections::HashMap; @@ -60,8 +60,8 @@ pub struct FieldPosition { /// Byte size of the field (4 for length prefix of variable-length /// fields, actual size for fixed-size fields). pub size: usize, - /// The TypeDef kind of the field. - pub kind: TypeDefKind, + /// The AlkType kind of the field. + pub kind: AlkTypeKind, } /// The result of building a layout: a map of field_path → FieldPosition @@ -139,22 +139,22 @@ pub struct LayoutBuilder { impl LayoutBuilder { /// Create a new LayoutBuilder from a schema. /// - /// The top-level schema must declare `TypeDef:Struct`. Endianness is + /// The top-level schema must declare `AlkType:Struct`. Endianness is /// parsed via [`Endian::from_schema`] (defaults to little-endian). /// /// # Errors /// - /// Returns [`TypedefError::Schema`] if the schema has no - /// `TypeDef:*` kind or the top-level kind is not `TypeDef:Struct`. - pub fn new(schema: &Value) -> Result { - let kind = schema::get_typedef_kind(schema) - .and_then(|s| s.parse::().ok()) + /// Returns [`AlkTypeError::Schema`] if the schema has no + /// `AlkType:*` kind or the top-level kind is not `AlkType:Struct`. + pub fn new(schema: &Value) -> Result { + let kind = schema::get_alktype_kind(schema) + .and_then(|s| s.parse::().ok()) .ok_or_else(|| { - TypedefError::Schema("top-level schema has no TypeDef:* kind".to_string()) + AlkTypeError::Schema("top-level schema has no AlkType:* kind".to_string()) })?; - if kind != TypeDefKind::Struct { - return Err(TypedefError::Schema(format!( - "LayoutBuilder requires a TypeDef:Struct at the top level, got {kind}" + if kind != AlkTypeKind::Struct { + return Err(AlkTypeError::Schema(format!( + "LayoutBuilder requires a AlkType:Struct at the top level, got {kind}" ))); } let endian = Endian::from_schema(schema); @@ -177,12 +177,12 @@ impl LayoutBuilder { /// /// # Errors /// - /// - [`TypedefError::Schema`] for malformed schemas (missing + /// - [`AlkTypeError::Schema`] for malformed schemas (missing /// `properties`, unknown kind, unresolvable `$ref`). - /// - [`TypedefError::Offset`] for missing variable-length field + /// - [`AlkTypeError::Offset`] for missing variable-length field /// sizes in `var_sizes`, missing discriminator values, or unknown /// discriminator values. - pub fn build(&self, var_sizes: &HashMap) -> Result { + pub fn build(&self, var_sizes: &HashMap) -> Result { let mut ctx = BuildCtx { root: &self.schema, var_sizes, @@ -210,7 +210,7 @@ struct BuildCtx<'a> { } impl<'a> BuildCtx<'a> { - /// Recurse into a `TypeDef:Struct`, appending `(field_path, FieldPosition)` + /// Recurse into a `AlkType:Struct`, appending `(field_path, FieldPosition)` /// pairs to `self.fields` and advancing `offset`. /// /// `prefix` is the dotted path prefix for nested fields (empty at the @@ -220,13 +220,13 @@ impl<'a> BuildCtx<'a> { schema: &Value, prefix: &str, offset: &mut usize, - ) -> Result<(), TypedefError> { + ) -> Result<(), AlkTypeError> { let properties = schema .as_object() .and_then(|o| o.get("properties")) .and_then(Value::as_object) .ok_or_else(|| { - TypedefError::Schema("struct schema has no 'properties' object".to_string()) + AlkTypeError::Schema("struct schema has no 'properties' object".to_string()) })?; let field_schemas: Vec<(String, Value)> = properties @@ -251,38 +251,38 @@ impl<'a> BuildCtx<'a> { field_schema: &Value, field_path: &str, offset: &mut usize, - ) -> Result<(), TypedefError> { - let kind = get_typedef_kind_loose_enum(field_schema).ok_or_else(|| TypedefError::Offset { + ) -> Result<(), AlkTypeError> { + let kind = get_alktype_kind_loose_enum(field_schema).ok_or_else(|| AlkTypeError::Offset { field_path: field_path.to_string(), - reason: "field schema has no TypeDef:* kind".to_string(), + reason: "field schema has no AlkType:* kind".to_string(), })?; match kind { - TypeDefKind::Struct => self.walk_struct(field_schema, field_path, offset), - TypeDefKind::Union => self.walk_union(field_schema, field_path, offset), - TypeDefKind::Array => self.walk_array(field_schema, field_path, offset), - TypeDefKind::String - | TypeDefKind::Bytes - | TypeDefKind::Timestamp - | TypeDefKind::Record => { + AlkTypeKind::Struct => self.walk_struct(field_schema, field_path, offset), + AlkTypeKind::Union => self.walk_union(field_schema, field_path, offset), + AlkTypeKind::Array => self.walk_array(field_schema, field_path, offset), + AlkTypeKind::String + | AlkTypeKind::Bytes + | AlkTypeKind::Timestamp + | AlkTypeKind::Record => { self.walk_variable(field_path, offset, kind) } k if k.is_fixed_size() => { - let size = k.type_size().ok_or_else(|| TypedefError::Offset { + let size = k.type_size().ok_or_else(|| AlkTypeError::Offset { field_path: field_path.to_string(), reason: format!("type_size returned None for fixed kind {k}"), })?; let start = *offset; *offset = start .checked_add(size) - .ok_or_else(|| TypedefError::Offset { + .ok_or_else(|| AlkTypeError::Offset { field_path: field_path.to_string(), reason: format!("offset {start} + size {size} overflows usize"), })?; self.push(field_path, start, size, k); Ok(()) } - _ => unreachable!("all TypeDefKind variants are covered above"), + _ => unreachable!("all AlkTypeKind variants are covered above"), } } @@ -292,26 +292,26 @@ impl<'a> BuildCtx<'a> { &mut self, field_path: &str, offset: &mut usize, - kind: TypeDefKind, - ) -> Result<(), TypedefError> { + kind: AlkTypeKind, + ) -> Result<(), AlkTypeError> { let data_size = self.var_sizes .get(field_path) .copied() - .ok_or_else(|| TypedefError::Offset { + .ok_or_else(|| AlkTypeError::Offset { field_path: field_path.to_string(), reason: "missing variable-length field size".to_string(), })?; let start = *offset; let total = U32_SIZE .checked_add(data_size) - .ok_or_else(|| TypedefError::Offset { + .ok_or_else(|| AlkTypeError::Offset { field_path: field_path.to_string(), reason: format!("prefix {U32_SIZE} + data size {data_size} overflows usize"), })?; *offset = start .checked_add(total) - .ok_or_else(|| TypedefError::Offset { + .ok_or_else(|| AlkTypeError::Offset { field_path: field_path.to_string(), reason: format!("offset {start} + total {total} overflows usize"), })?; @@ -319,36 +319,36 @@ impl<'a> BuildCtx<'a> { Ok(()) } - /// Compute the layout for a `TypeDef:Array` field. + /// Compute the layout for a `AlkType:Array` field. fn walk_array( &mut self, field_schema: &Value, field_path: &str, offset: &mut usize, - ) -> Result<(), TypedefError> { + ) -> Result<(), AlkTypeError> { let obj = field_schema .as_object() - .ok_or_else(|| TypedefError::Offset { + .ok_or_else(|| AlkTypeError::Offset { field_path: field_path.to_string(), reason: "array schema is not an object".to_string(), })?; - let items = obj.get("items").ok_or_else(|| TypedefError::Offset { + let items = obj.get("items").ok_or_else(|| AlkTypeError::Offset { field_path: field_path.to_string(), reason: "TArray is missing 'items'".to_string(), })?; let element_schema = - resolve_ref_or_inline(items, self.root).ok_or_else(|| TypedefError::Offset { + resolve_ref_or_inline(items, self.root).ok_or_else(|| AlkTypeError::Offset { field_path: field_path.to_string(), reason: "could not resolve TArray items schema".to_string(), })?; - let elem_kind = get_typedef_kind_loose_enum(element_schema).ok_or_else(|| TypedefError::Offset { + let elem_kind = get_alktype_kind_loose_enum(element_schema).ok_or_else(|| AlkTypeError::Offset { field_path: field_path.to_string(), - reason: "TArray element schema has no TypeDef:* kind".to_string(), + reason: "TArray element schema has no AlkType:* kind".to_string(), })?; if !elem_kind.is_fixed_size() { - return Err(TypedefError::Offset { + return Err(AlkTypeError::Offset { field_path: field_path.to_string(), reason: format!( "TArray of variable-length element kind {elem_kind} is not supported (OQ-069)" @@ -356,7 +356,7 @@ impl<'a> BuildCtx<'a> { }); } - let elem_size = elem_kind.type_size().ok_or_else(|| TypedefError::Offset { + let elem_size = elem_kind.type_size().ok_or_else(|| AlkTypeError::Offset { field_path: field_path.to_string(), reason: format!("element kind {elem_kind} has no fixed size"), })?; @@ -380,14 +380,14 @@ impl<'a> BuildCtx<'a> { let elem_offset = start .checked_add( i.checked_mul(elem_size) - .ok_or_else(|| TypedefError::Offset { + .ok_or_else(|| AlkTypeError::Offset { field_path: field_path.to_string(), reason: format!( "element index {i} × size {elem_size} overflows usize" ), })?, ) - .ok_or_else(|| TypedefError::Offset { + .ok_or_else(|| AlkTypeError::Offset { field_path: field_path.to_string(), reason: format!("element offset {start} + {i}×{elem_size} overflows usize"), })?; @@ -400,13 +400,13 @@ impl<'a> BuildCtx<'a> { } let array_size = count .checked_mul(elem_size) - .ok_or_else(|| TypedefError::Offset { + .ok_or_else(|| AlkTypeError::Offset { field_path: field_path.to_string(), reason: format!("array size {count} × {elem_size} overflows usize"), })?; *offset = start .checked_add(array_size) - .ok_or_else(|| TypedefError::Offset { + .ok_or_else(|| AlkTypeError::Offset { field_path: field_path.to_string(), reason: format!("offset {start} + array size {array_size} overflows usize"), })?; @@ -416,14 +416,14 @@ impl<'a> BuildCtx<'a> { self.var_sizes .get(field_path) .copied() - .ok_or_else(|| TypedefError::Offset { + .ok_or_else(|| AlkTypeError::Offset { field_path: field_path.to_string(), reason: "missing variable-count array element data size".to_string(), })?; let start = *offset; let total = U32_SIZE .checked_add(data_size) - .ok_or_else(|| TypedefError::Offset { + .ok_or_else(|| AlkTypeError::Offset { field_path: field_path.to_string(), reason: format!( "count prefix {U32_SIZE} + data size {data_size} overflows usize" @@ -431,28 +431,28 @@ impl<'a> BuildCtx<'a> { })?; *offset = start .checked_add(total) - .ok_or_else(|| TypedefError::Offset { + .ok_or_else(|| AlkTypeError::Offset { field_path: field_path.to_string(), reason: format!("offset {start} + total {total} overflows usize"), })?; - self.push(field_path, start, U32_SIZE, TypeDefKind::Array); + self.push(field_path, start, U32_SIZE, AlkTypeKind::Array); Ok(()) } } - /// Compute the layout for a `TypeDef:Union` field. + /// Compute the layout for a `AlkType:Union` field. fn walk_union( &mut self, field_schema: &Value, field_path: &str, offset: &mut usize, - ) -> Result<(), TypedefError> { + ) -> Result<(), AlkTypeError> { let disc = schema::parse_discriminator(field_schema)?; let mapping = field_schema .as_object() .and_then(|o| o.get("mapping")) .and_then(Value::as_object) - .ok_or_else(|| TypedefError::Offset { + .ok_or_else(|| AlkTypeError::Offset { field_path: field_path.to_string(), reason: "TUnion is missing 'mapping' object".to_string(), })?; @@ -474,11 +474,11 @@ impl<'a> BuildCtx<'a> { &mut self, field_path: &str, offset: &mut usize, - disc_type: TypeDefKind, + disc_type: AlkTypeKind, disc_off: usize, mapping: &serde_json::Map, - ) -> Result<(), TypedefError> { - let disc_size = disc_type.type_size().ok_or_else(|| TypedefError::Offset { + ) -> Result<(), AlkTypeError> { + let disc_size = disc_type.type_size().ok_or_else(|| AlkTypeError::Offset { field_path: field_path.to_string(), reason: format!("discriminator type {disc_type} has no fixed size"), })?; @@ -488,7 +488,7 @@ impl<'a> BuildCtx<'a> { self.var_sizes .get(&disc_key) .copied() - .ok_or_else(|| TypedefError::Offset { + .ok_or_else(|| AlkTypeError::Offset { field_path: field_path.to_string(), reason: format!("missing discriminator value at key '{disc_key}'"), })?; @@ -497,7 +497,7 @@ impl<'a> BuildCtx<'a> { let disc_abs_offset = union_start .checked_add(disc_off) - .ok_or_else(|| TypedefError::Offset { + .ok_or_else(|| AlkTypeError::Offset { field_path: field_path.to_string(), reason: format!( "union offset {union_start} + disc offset {disc_off} overflows usize" @@ -508,31 +508,31 @@ impl<'a> BuildCtx<'a> { let variant_key = disc_value.to_string(); let variant_schema = mapping .get(&variant_key) - .ok_or_else(|| TypedefError::Offset { + .ok_or_else(|| AlkTypeError::Offset { field_path: field_path.to_string(), reason: format!("unknown discriminator value: {variant_key}"), })?; let resolved = resolve_ref_or_inline(variant_schema, self.root).ok_or_else(|| { - TypedefError::Offset { + AlkTypeError::Offset { field_path: field_path.to_string(), reason: "could not resolve TUnion variant schema ($ref not found)".to_string(), } })?; - let v_kind = get_typedef_kind_loose_enum(resolved).ok_or_else(|| TypedefError::Offset { + let v_kind = get_alktype_kind_loose_enum(resolved).ok_or_else(|| AlkTypeError::Offset { field_path: field_path.to_string(), - reason: "TUnion variant schema has no TypeDef:* kind".to_string(), + reason: "TUnion variant schema has no AlkType:* kind".to_string(), })?; - if v_kind != TypeDefKind::Struct { - return Err(TypedefError::Offset { + if v_kind != AlkTypeKind::Struct { + return Err(AlkTypeError::Offset { field_path: field_path.to_string(), - reason: format!("TUnion variant must be TypeDef:Struct, got {v_kind}"), + reason: format!("TUnion variant must be AlkType:Struct, got {v_kind}"), }); } let variant_start = disc_abs_offset .checked_add(disc_size) - .ok_or_else(|| TypedefError::Offset { + .ok_or_else(|| AlkTypeError::Offset { field_path: field_path.to_string(), reason: format!( "variant start {disc_abs_offset} + disc size {disc_size} overflows usize" @@ -553,13 +553,13 @@ impl<'a> BuildCtx<'a> { field_path: &str, offset: &mut usize, mapping: &serde_json::Map, - ) -> Result<(), TypedefError> { + ) -> Result<(), AlkTypeError> { let variant_key = format!("{field_path}.{VARIANT_KEY}"); let variant_index = self.var_sizes .get(&variant_key) .copied() - .ok_or_else(|| TypedefError::Offset { + .ok_or_else(|| AlkTypeError::Offset { field_path: field_path.to_string(), reason: format!("missing variant index at key '{variant_key}'"), })?; @@ -567,7 +567,7 @@ impl<'a> BuildCtx<'a> { mapping .iter() .nth(variant_index) - .ok_or_else(|| TypedefError::Offset { + .ok_or_else(|| AlkTypeError::Offset { field_path: field_path.to_string(), reason: format!( "variant index {variant_index} out of range (mapping has {} entries)", @@ -576,19 +576,19 @@ impl<'a> BuildCtx<'a> { })?; let variant_schema = variant_entry.1; let resolved = resolve_ref_or_inline(variant_schema, self.root).ok_or_else(|| { - TypedefError::Offset { + AlkTypeError::Offset { field_path: field_path.to_string(), reason: "could not resolve TUnion variant schema ($ref not found)".to_string(), } })?; - let v_kind = get_typedef_kind_loose_enum(resolved).ok_or_else(|| TypedefError::Offset { + let v_kind = get_alktype_kind_loose_enum(resolved).ok_or_else(|| AlkTypeError::Offset { field_path: field_path.to_string(), - reason: "TUnion variant schema has no TypeDef:* kind".to_string(), + reason: "TUnion variant schema has no AlkType:* kind".to_string(), })?; - if v_kind != TypeDefKind::Struct { - return Err(TypedefError::Offset { + if v_kind != AlkTypeKind::Struct { + return Err(AlkTypeError::Offset { field_path: field_path.to_string(), - reason: format!("TUnion variant must be TypeDef:Struct, got {v_kind}"), + reason: format!("TUnion variant must be AlkType:Struct, got {v_kind}"), }); } self.walk_struct(resolved, field_path, offset)?; @@ -596,7 +596,7 @@ impl<'a> BuildCtx<'a> { } /// Push a `(field_path, FieldPosition)` pair onto the fields vec. - fn push(&mut self, path: &str, offset: usize, size: usize, kind: TypeDefKind) { + fn push(&mut self, path: &str, offset: usize, size: usize, kind: AlkTypeKind) { self.fields.push(( path.to_string(), FieldPosition { @@ -627,11 +627,11 @@ mod tests { #[test] fn fixed_fields_packed_no_alignment_padding() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "a": { "TypeDef:Uint8": true }, - "b": { "TypeDef:Uint32": true }, - "c": { "TypeDef:Uint16": true } + "a": { "AlkType:Uint8": true }, + "b": { "AlkType:Uint32": true }, + "c": { "AlkType:Uint16": true } } }); let layout = build(&schema, &var_sizes(&[])); @@ -640,7 +640,7 @@ mod tests { Some(&FieldPosition { offset: 0, size: 1, - kind: TypeDefKind::Uint8 + kind: AlkTypeKind::Uint8 }) ); assert_eq!( @@ -648,7 +648,7 @@ mod tests { Some(&FieldPosition { offset: 1, size: 4, - kind: TypeDefKind::Uint32 + kind: AlkTypeKind::Uint32 }) ); assert_eq!( @@ -656,7 +656,7 @@ mod tests { Some(&FieldPosition { offset: 5, size: 2, - kind: TypeDefKind::Uint16 + kind: AlkTypeKind::Uint16 }) ); assert_eq!(layout.total_size(), 7); @@ -665,11 +665,11 @@ mod tests { #[test] fn spec_example_u8_u32_string_total_19() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "flag": { "TypeDef:Uint8": true }, - "id": { "TypeDef:Uint32": true }, - "payload": { "TypeDef:String": true } + "flag": { "AlkType:Uint8": true }, + "id": { "AlkType:Uint32": true }, + "payload": { "AlkType:String": true } } }); let layout = build(&schema, &var_sizes(&[("payload", 10)])); @@ -678,7 +678,7 @@ mod tests { Some(&FieldPosition { offset: 0, size: 1, - kind: TypeDefKind::Uint8 + kind: AlkTypeKind::Uint8 }) ); assert_eq!( @@ -686,7 +686,7 @@ mod tests { Some(&FieldPosition { offset: 1, size: 4, - kind: TypeDefKind::Uint32 + kind: AlkTypeKind::Uint32 }) ); assert_eq!( @@ -694,7 +694,7 @@ mod tests { Some(&FieldPosition { offset: 5, size: 4, - kind: TypeDefKind::String + kind: AlkTypeKind::String }) ); assert_eq!(layout.total_size(), 19); @@ -703,10 +703,10 @@ mod tests { #[test] fn variable_length_field_shifts_subsequent_fields() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "name": { "TypeDef:String": true }, - "tail": { "TypeDef:Uint8": true } + "name": { "AlkType:String": true }, + "tail": { "AlkType:Uint8": true } } }); let layout = build(&schema, &var_sizes(&[("name", 5)])); @@ -715,7 +715,7 @@ mod tests { Some(&FieldPosition { offset: 0, size: 4, - kind: TypeDefKind::String + kind: AlkTypeKind::String }) ); assert_eq!( @@ -723,7 +723,7 @@ mod tests { Some(&FieldPosition { offset: 9, size: 1, - kind: TypeDefKind::Uint8 + kind: AlkTypeKind::Uint8 }) ); assert_eq!(layout.total_size(), 10); @@ -732,9 +732,9 @@ mod tests { #[test] fn bytes_field_uses_var_sizes() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "blob": { "TypeDef:Bytes": true } + "blob": { "AlkType:Bytes": true } } }); let layout = build(&schema, &var_sizes(&[("blob", 3)])); @@ -743,7 +743,7 @@ mod tests { Some(&FieldPosition { offset: 0, size: 4, - kind: TypeDefKind::Bytes + kind: AlkTypeKind::Bytes }) ); assert_eq!(layout.total_size(), 7); @@ -752,9 +752,9 @@ mod tests { #[test] fn timestamp_field_uses_var_sizes() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "ts": { "TypeDef:Timestamp": true } + "ts": { "AlkType:Timestamp": true } } }); let layout = build(&schema, &var_sizes(&[("ts", 20)])); @@ -763,7 +763,7 @@ mod tests { Some(&FieldPosition { offset: 0, size: 4, - kind: TypeDefKind::Timestamp + kind: AlkTypeKind::Timestamp }) ); assert_eq!(layout.total_size(), 24); @@ -772,11 +772,11 @@ mod tests { #[test] fn record_field_uses_var_sizes() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { "counts": { - "TypeDef:Record": true, - "values": { "TypeDef:Uint32": true } + "AlkType:Record": true, + "values": { "AlkType:Uint32": true } } } }); @@ -786,7 +786,7 @@ mod tests { Some(&FieldPosition { offset: 0, size: 4, - kind: TypeDefKind::Record + kind: AlkTypeKind::Record }) ); assert_eq!(layout.total_size(), 104); @@ -795,15 +795,15 @@ mod tests { #[test] fn missing_var_size_returns_offset_error() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "name": { "TypeDef:String": true } + "name": { "AlkType:String": true } } }); let builder = LayoutBuilder::new(&schema).expect("builder"); let err = builder.build(&var_sizes(&[])).unwrap_err(); match err { - TypedefError::Offset { field_path, reason } => { + AlkTypeError::Offset { field_path, reason } => { assert_eq!(field_path, "name"); assert!( reason.contains("missing variable-length field size"), @@ -817,16 +817,16 @@ mod tests { #[test] fn nested_struct_dotted_paths() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { "header": { - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "magic": { "TypeDef:Uint32": true }, - "version": { "TypeDef:Uint8": true } + "magic": { "AlkType:Uint32": true }, + "version": { "AlkType:Uint8": true } } }, - "body": { "TypeDef:Uint32": true } + "body": { "AlkType:Uint32": true } } }); let layout = build(&schema, &var_sizes(&[])); @@ -835,7 +835,7 @@ mod tests { Some(&FieldPosition { offset: 0, size: 4, - kind: TypeDefKind::Uint32 + kind: AlkTypeKind::Uint32 }) ); assert_eq!( @@ -843,7 +843,7 @@ mod tests { Some(&FieldPosition { offset: 4, size: 1, - kind: TypeDefKind::Uint8 + kind: AlkTypeKind::Uint8 }) ); assert_eq!( @@ -851,7 +851,7 @@ mod tests { Some(&FieldPosition { offset: 5, size: 4, - kind: TypeDefKind::Uint32 + kind: AlkTypeKind::Uint32 }) ); assert_eq!(layout.total_size(), 9); @@ -860,16 +860,16 @@ mod tests { #[test] fn nested_struct_with_variable_field() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { "header": { - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "id": { "TypeDef:Uint8": true }, - "name": { "TypeDef:String": true } + "id": { "AlkType:Uint8": true }, + "name": { "AlkType:String": true } } }, - "tail": { "TypeDef:Uint8": true } + "tail": { "AlkType:Uint8": true } } }); let layout = build(&schema, &var_sizes(&[("header.name", 3)])); @@ -878,7 +878,7 @@ mod tests { Some(&FieldPosition { offset: 0, size: 1, - kind: TypeDefKind::Uint8 + kind: AlkTypeKind::Uint8 }) ); assert_eq!( @@ -886,7 +886,7 @@ mod tests { Some(&FieldPosition { offset: 1, size: 4, - kind: TypeDefKind::String + kind: AlkTypeKind::String }) ); assert_eq!( @@ -894,7 +894,7 @@ mod tests { Some(&FieldPosition { offset: 8, size: 1, - kind: TypeDefKind::Uint8 + kind: AlkTypeKind::Uint8 }) ); assert_eq!(layout.total_size(), 9); @@ -903,11 +903,11 @@ mod tests { #[test] fn array_fixed_count_element_offsets() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { "vals": { - "TypeDef:Array": true, - "items": { "TypeDef:Uint32": true }, + "AlkType:Array": true, + "items": { "AlkType:Uint32": true }, "minItems": 3, "maxItems": 3 } @@ -919,7 +919,7 @@ mod tests { Some(&FieldPosition { offset: 0, size: 4, - kind: TypeDefKind::Uint32 + kind: AlkTypeKind::Uint32 }) ); assert_eq!( @@ -927,7 +927,7 @@ mod tests { Some(&FieldPosition { offset: 4, size: 4, - kind: TypeDefKind::Uint32 + kind: AlkTypeKind::Uint32 }) ); assert_eq!( @@ -935,7 +935,7 @@ mod tests { Some(&FieldPosition { offset: 8, size: 4, - kind: TypeDefKind::Uint32 + kind: AlkTypeKind::Uint32 }) ); assert_eq!(layout.total_size(), 12); @@ -944,12 +944,12 @@ mod tests { #[test] fn array_fixed_count_after_preceding_field() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "id": { "TypeDef:Uint8": true }, + "id": { "AlkType:Uint8": true }, "vals": { - "TypeDef:Array": true, - "items": { "TypeDef:Uint16": true }, + "AlkType:Array": true, + "items": { "AlkType:Uint16": true }, "minItems": 2, "maxItems": 2 } @@ -961,7 +961,7 @@ mod tests { Some(&FieldPosition { offset: 0, size: 1, - kind: TypeDefKind::Uint8 + kind: AlkTypeKind::Uint8 }) ); assert_eq!( @@ -969,7 +969,7 @@ mod tests { Some(&FieldPosition { offset: 1, size: 2, - kind: TypeDefKind::Uint16 + kind: AlkTypeKind::Uint16 }) ); assert_eq!( @@ -977,7 +977,7 @@ mod tests { Some(&FieldPosition { offset: 3, size: 2, - kind: TypeDefKind::Uint16 + kind: AlkTypeKind::Uint16 }) ); assert_eq!(layout.total_size(), 5); @@ -986,11 +986,11 @@ mod tests { #[test] fn array_variable_count_uses_count_prefix() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { "vals": { - "TypeDef:Array": true, - "items": { "TypeDef:Uint32": true } + "AlkType:Array": true, + "items": { "AlkType:Uint32": true } } } }); @@ -1000,7 +1000,7 @@ mod tests { Some(&FieldPosition { offset: 0, size: 4, - kind: TypeDefKind::Array + kind: AlkTypeKind::Array }) ); assert_eq!(layout.total_size(), 16); @@ -1009,34 +1009,34 @@ mod tests { #[test] fn array_variable_count_missing_size_is_offset_error() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { "vals": { - "TypeDef:Array": true, - "items": { "TypeDef:Uint32": true } + "AlkType:Array": true, + "items": { "AlkType:Uint32": true } } } }); let builder = LayoutBuilder::new(&schema).expect("builder"); let err = builder.build(&var_sizes(&[])).unwrap_err(); - assert!(matches!(err, TypedefError::Offset { .. })); + assert!(matches!(err, AlkTypeError::Offset { .. })); } #[test] fn array_variable_length_element_is_not_supported() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { "items": { - "TypeDef:Array": true, - "items": { "TypeDef:String": true } + "AlkType:Array": true, + "items": { "AlkType:String": true } } } }); let builder = LayoutBuilder::new(&schema).expect("builder"); let err = builder.build(&var_sizes(&[("items", 10)])).unwrap_err(); match err { - TypedefError::Offset { reason, .. } => { + AlkTypeError::Offset { reason, .. } => { assert!(reason.contains("OQ-069"), "reason: {reason}"); } other => panic!("expected Offset, got {other:?}"), @@ -1046,14 +1046,14 @@ mod tests { #[test] fn union_byte_discriminator_sftp_pattern() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { "payload": { - "TypeDef:Union": true, + "AlkType:Union": true, "discriminator": { "kind": "byte", "offset": 0, - "type": "TypeDef:Uint8" + "type": "AlkType:Uint8" }, "mapping": { "5": { "$ref": "#/$defs/Read" }, @@ -1063,18 +1063,18 @@ mod tests { }, "$defs": { "Read": { - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "handle": { "TypeDef:Uint32": true }, - "length": { "TypeDef:Uint32": true } + "handle": { "AlkType:Uint32": true }, + "length": { "AlkType:Uint32": true } } }, "Write": { - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "handle": { "TypeDef:Uint32": true }, - "length": { "TypeDef:Uint32": true }, - "data": { "TypeDef:Uint32": true } + "handle": { "AlkType:Uint32": true }, + "length": { "AlkType:Uint32": true }, + "data": { "AlkType:Uint32": true } } } } @@ -1086,7 +1086,7 @@ mod tests { Some(&FieldPosition { offset: 0, size: 1, - kind: TypeDefKind::Uint8 + kind: AlkTypeKind::Uint8 }) ); assert_eq!( @@ -1094,7 +1094,7 @@ mod tests { Some(&FieldPosition { offset: 1, size: 4, - kind: TypeDefKind::Uint32 + kind: AlkTypeKind::Uint32 }) ); assert_eq!( @@ -1102,7 +1102,7 @@ mod tests { Some(&FieldPosition { offset: 5, size: 4, - kind: TypeDefKind::Uint32 + kind: AlkTypeKind::Uint32 }) ); assert_eq!(layout.total_size(), 9); @@ -1111,14 +1111,14 @@ mod tests { #[test] fn union_byte_discriminator_write_variant() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { "payload": { - "TypeDef:Union": true, + "AlkType:Union": true, "discriminator": { "kind": "byte", "offset": 0, - "type": "TypeDef:Uint8" + "type": "AlkType:Uint8" }, "mapping": { "5": { "$ref": "#/$defs/Read" }, @@ -1128,18 +1128,18 @@ mod tests { }, "$defs": { "Read": { - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "handle": { "TypeDef:Uint32": true }, - "length": { "TypeDef:Uint32": true } + "handle": { "AlkType:Uint32": true }, + "length": { "AlkType:Uint32": true } } }, "Write": { - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "handle": { "TypeDef:Uint32": true }, - "length": { "TypeDef:Uint32": true }, - "data": { "TypeDef:Uint32": true } + "handle": { "AlkType:Uint32": true }, + "length": { "AlkType:Uint32": true }, + "data": { "AlkType:Uint32": true } } } } @@ -1151,7 +1151,7 @@ mod tests { Some(&FieldPosition { offset: 0, size: 1, - kind: TypeDefKind::Uint8 + kind: AlkTypeKind::Uint8 }) ); assert_eq!( @@ -1159,7 +1159,7 @@ mod tests { Some(&FieldPosition { offset: 9, size: 4, - kind: TypeDefKind::Uint32 + kind: AlkTypeKind::Uint32 }) ); assert_eq!(layout.total_size(), 13); @@ -1168,14 +1168,14 @@ mod tests { #[test] fn union_byte_discriminator_with_variable_variant_field() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { "packet": { - "TypeDef:Union": true, + "AlkType:Union": true, "discriminator": { "kind": "byte", "offset": 0, - "type": "TypeDef:Uint8" + "type": "AlkType:Uint8" }, "mapping": { "5": { "$ref": "#/$defs/Read" } @@ -1184,10 +1184,10 @@ mod tests { }, "$defs": { "Read": { - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "handle": { "TypeDef:Uint32": true }, - "path": { "TypeDef:String": true } + "handle": { "AlkType:Uint32": true }, + "path": { "AlkType:String": true } } } } @@ -1199,7 +1199,7 @@ mod tests { Some(&FieldPosition { offset: 0, size: 1, - kind: TypeDefKind::Uint8 + kind: AlkTypeKind::Uint8 }) ); assert_eq!( @@ -1207,7 +1207,7 @@ mod tests { Some(&FieldPosition { offset: 1, size: 4, - kind: TypeDefKind::Uint32 + kind: AlkTypeKind::Uint32 }) ); assert_eq!( @@ -1215,7 +1215,7 @@ mod tests { Some(&FieldPosition { offset: 5, size: 4, - kind: TypeDefKind::String + kind: AlkTypeKind::String }) ); assert_eq!(layout.total_size(), 17); @@ -1224,14 +1224,14 @@ mod tests { #[test] fn union_byte_discriminator_unknown_value_is_offset_error() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { "payload": { - "TypeDef:Union": true, + "AlkType:Union": true, "discriminator": { "kind": "byte", "offset": 0, - "type": "TypeDef:Uint8" + "type": "AlkType:Uint8" }, "mapping": { "5": { "$ref": "#/$defs/Read" } @@ -1240,8 +1240,8 @@ mod tests { }, "$defs": { "Read": { - "TypeDef:Struct": true, - "properties": { "x": { "TypeDef:Uint8": true } } + "AlkType:Struct": true, + "properties": { "x": { "AlkType:Uint8": true } } } } }); @@ -1249,7 +1249,7 @@ mod tests { let builder = LayoutBuilder::new(&schema).expect("builder"); let err = builder.build(&vs).unwrap_err(); match err { - TypedefError::Offset { reason, .. } => { + AlkTypeError::Offset { reason, .. } => { assert!(reason.contains("99"), "reason: {reason}"); } other => panic!("expected Offset, got {other:?}"), @@ -1259,14 +1259,14 @@ mod tests { #[test] fn union_byte_discriminator_missing_value_is_offset_error() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { "payload": { - "TypeDef:Union": true, + "AlkType:Union": true, "discriminator": { "kind": "byte", "offset": 0, - "type": "TypeDef:Uint8" + "type": "AlkType:Uint8" }, "mapping": { "5": { "$ref": "#/$defs/Read" } @@ -1275,23 +1275,23 @@ mod tests { }, "$defs": { "Read": { - "TypeDef:Struct": true, - "properties": { "x": { "TypeDef:Uint8": true } } + "AlkType:Struct": true, + "properties": { "x": { "AlkType:Uint8": true } } } } }); let builder = LayoutBuilder::new(&schema).expect("builder"); let err = builder.build(&var_sizes(&[])).unwrap_err(); - assert!(matches!(err, TypedefError::Offset { .. })); + assert!(matches!(err, AlkTypeError::Offset { .. })); } #[test] fn union_field_name_discriminator() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { "event": { - "TypeDef:Union": true, + "AlkType:Union": true, "discriminator": { "kind": "field", "name": "type" }, "mapping": { "read": { "$ref": "#/$defs/Read" }, @@ -1301,18 +1301,18 @@ mod tests { }, "$defs": { "Read": { - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "type": { "TypeDef:Uint8": true }, - "handle": { "TypeDef:Uint32": true } + "type": { "AlkType:Uint8": true }, + "handle": { "AlkType:Uint32": true } } }, "Write": { - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "type": { "TypeDef:Uint8": true }, - "handle": { "TypeDef:Uint32": true }, - "length": { "TypeDef:Uint32": true } + "type": { "AlkType:Uint8": true }, + "handle": { "AlkType:Uint32": true }, + "length": { "AlkType:Uint32": true } } } } @@ -1324,7 +1324,7 @@ mod tests { Some(&FieldPosition { offset: 0, size: 1, - kind: TypeDefKind::Uint8 + kind: AlkTypeKind::Uint8 }) ); assert_eq!( @@ -1332,7 +1332,7 @@ mod tests { Some(&FieldPosition { offset: 1, size: 4, - kind: TypeDefKind::Uint32 + kind: AlkTypeKind::Uint32 }) ); assert_eq!(layout.total_size(), 5); @@ -1341,10 +1341,10 @@ mod tests { #[test] fn union_field_name_discriminator_write_variant() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { "event": { - "TypeDef:Union": true, + "AlkType:Union": true, "discriminator": { "kind": "field", "name": "type" }, "mapping": { "read": { "$ref": "#/$defs/Read" }, @@ -1354,18 +1354,18 @@ mod tests { }, "$defs": { "Read": { - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "type": { "TypeDef:Uint8": true }, - "handle": { "TypeDef:Uint32": true } + "type": { "AlkType:Uint8": true }, + "handle": { "AlkType:Uint32": true } } }, "Write": { - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "type": { "TypeDef:Uint8": true }, - "handle": { "TypeDef:Uint32": true }, - "length": { "TypeDef:Uint32": true } + "type": { "AlkType:Uint8": true }, + "handle": { "AlkType:Uint32": true }, + "length": { "AlkType:Uint32": true } } } } @@ -1377,7 +1377,7 @@ mod tests { Some(&FieldPosition { offset: 5, size: 4, - kind: TypeDefKind::Uint32 + kind: AlkTypeKind::Uint32 }) ); assert_eq!(layout.total_size(), 9); @@ -1386,10 +1386,10 @@ mod tests { #[test] fn union_field_name_missing_variant_index_is_offset_error() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { "event": { - "TypeDef:Union": true, + "AlkType:Union": true, "discriminator": { "kind": "field", "name": "type" }, "mapping": { "read": { "$ref": "#/$defs/Read" } @@ -1398,23 +1398,23 @@ mod tests { }, "$defs": { "Read": { - "TypeDef:Struct": true, - "properties": { "type": { "TypeDef:Uint8": true } } + "AlkType:Struct": true, + "properties": { "type": { "AlkType:Uint8": true } } } } }); let builder = LayoutBuilder::new(&schema).expect("builder"); let err = builder.build(&var_sizes(&[])).unwrap_err(); - assert!(matches!(err, TypedefError::Offset { .. })); + assert!(matches!(err, AlkTypeError::Offset { .. })); } #[test] fn union_field_name_variant_index_out_of_range_is_offset_error() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { "event": { - "TypeDef:Union": true, + "AlkType:Union": true, "discriminator": { "kind": "field", "name": "type" }, "mapping": { "read": { "$ref": "#/$defs/Read" } @@ -1423,25 +1423,25 @@ mod tests { }, "$defs": { "Read": { - "TypeDef:Struct": true, - "properties": { "type": { "TypeDef:Uint8": true } } + "AlkType:Struct": true, + "properties": { "type": { "AlkType:Uint8": true } } } } }); let vs = var_sizes(&[("event.__variant", 5)]); let builder = LayoutBuilder::new(&schema).expect("builder"); let err = builder.build(&vs).unwrap_err(); - assert!(matches!(err, TypedefError::Offset { .. })); + assert!(matches!(err, AlkTypeError::Offset { .. })); } #[test] fn iter_returns_fields_in_layout_order() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "a": { "TypeDef:Uint8": true }, - "b": { "TypeDef:Uint32": true }, - "c": { "TypeDef:Uint16": true } + "a": { "AlkType:Uint8": true }, + "b": { "AlkType:Uint32": true }, + "c": { "AlkType:Uint16": true } } }); let layout = build(&schema, &var_sizes(&[])); @@ -1452,16 +1452,16 @@ mod tests { #[test] fn iter_includes_nested_struct_fields() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { "header": { - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "magic": { "TypeDef:Uint32": true }, - "version": { "TypeDef:Uint8": true } + "magic": { "AlkType:Uint32": true }, + "version": { "AlkType:Uint8": true } } }, - "body": { "TypeDef:Uint32": true } + "body": { "AlkType:Uint32": true } } }); let layout = build(&schema, &var_sizes(&[])); @@ -1472,9 +1472,9 @@ mod tests { #[test] fn get_returns_none_for_unknown_path() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "a": { "TypeDef:Uint8": true } + "a": { "AlkType:Uint8": true } } }); let layout = build(&schema, &var_sizes(&[])); @@ -1484,10 +1484,10 @@ mod tests { #[test] fn endian_parsed_from_schema() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "endian": "big", "properties": { - "id": { "TypeDef:Uint32": true } + "id": { "AlkType:Uint32": true } } }); let builder = LayoutBuilder::new(&schema).expect("builder"); @@ -1497,9 +1497,9 @@ mod tests { #[test] fn endian_defaults_to_little() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "id": { "TypeDef:Uint32": true } + "id": { "AlkType:Uint32": true } } }); let builder = LayoutBuilder::new(&schema).expect("builder"); @@ -1508,45 +1508,45 @@ mod tests { #[test] fn new_rejects_non_struct_top_level() { - let schema = json!({ "TypeDef:Uint32": true }); + let schema = json!({ "AlkType:Uint32": true }); let err = LayoutBuilder::new(&schema).unwrap_err(); - assert!(matches!(err, TypedefError::Schema(_))); + assert!(matches!(err, AlkTypeError::Schema(_))); } #[test] - fn new_rejects_missing_typedef_kind() { + fn new_rejects_missing_alktype_kind() { let schema = json!({ "type": "object", "properties": {} }); let err = LayoutBuilder::new(&schema).unwrap_err(); - assert!(matches!(err, TypedefError::Schema(_))); + assert!(matches!(err, AlkTypeError::Schema(_))); } #[test] fn build_rejects_struct_without_properties() { - let schema = json!({ "TypeDef:Struct": true }); + let schema = json!({ "AlkType:Struct": true }); let builder = LayoutBuilder::new(&schema).expect("builder"); let err = builder.build(&var_sizes(&[])).unwrap_err(); - assert!(matches!(err, TypedefError::Schema(_))); + assert!(matches!(err, AlkTypeError::Schema(_))); } #[test] - fn build_rejects_field_without_typedef_kind() { + fn build_rejects_field_without_alktype_kind() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { "a": { "type": "integer" } } }); let builder = LayoutBuilder::new(&schema).expect("builder"); let err = builder.build(&var_sizes(&[])).unwrap_err(); - assert!(matches!(err, TypedefError::Offset { .. })); + assert!(matches!(err, AlkTypeError::Offset { .. })); } #[test] fn object_form_keyword_is_recognized() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "name": { "TypeDef:String": { "encoding": "length-prefixed" } } + "name": { "AlkType:String": { "encoding": "length-prefixed" } } } }); let layout = build(&schema, &var_sizes(&[("name", 5)])); @@ -1555,7 +1555,7 @@ mod tests { Some(&FieldPosition { offset: 0, size: 4, - kind: TypeDefKind::String + kind: AlkTypeKind::String }) ); assert_eq!(layout.total_size(), 9); @@ -1564,18 +1564,18 @@ mod tests { #[test] fn all_fixed_size_kinds_get_correct_sizes() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "i8": { "TypeDef:Int8": true }, - "i16": { "TypeDef:Int16": true }, - "i32": { "TypeDef:Int32": true }, - "u8": { "TypeDef:Uint8": true }, - "u16": { "TypeDef:Uint16": true }, - "u32": { "TypeDef:Uint32": true }, - "f32": { "TypeDef:Float32": true }, - "f64": { "TypeDef:Float64": true }, - "b": { "TypeDef:Boolean": true }, - "e": { "TypeDef:Enum": true } + "i8": { "AlkType:Int8": true }, + "i16": { "AlkType:Int16": true }, + "i32": { "AlkType:Int32": true }, + "u8": { "AlkType:Uint8": true }, + "u16": { "AlkType:Uint16": true }, + "u32": { "AlkType:Uint32": true }, + "f32": { "AlkType:Float32": true }, + "f64": { "AlkType:Float64": true }, + "b": { "AlkType:Boolean": true }, + "e": { "AlkType:Enum": true } } }); let layout = build(&schema, &var_sizes(&[])); @@ -1595,7 +1595,7 @@ mod tests { #[test] fn empty_struct_produces_zero_size_layout() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": {} }); let layout = build(&schema, &var_sizes(&[])); @@ -1606,20 +1606,20 @@ mod tests { #[test] fn inline_variant_schema_works_without_ref() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { "packet": { - "TypeDef:Union": true, + "AlkType:Union": true, "discriminator": { "kind": "byte", "offset": 0, - "type": "TypeDef:Uint8" + "type": "AlkType:Uint8" }, "mapping": { "5": { - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "x": { "TypeDef:Uint32": true } + "x": { "AlkType:Uint32": true } } } } @@ -1633,7 +1633,7 @@ mod tests { Some(&FieldPosition { offset: 0, size: 1, - kind: TypeDefKind::Uint8 + kind: AlkTypeKind::Uint8 }) ); assert_eq!( @@ -1641,7 +1641,7 @@ mod tests { Some(&FieldPosition { offset: 1, size: 4, - kind: TypeDefKind::Uint32 + kind: AlkTypeKind::Uint32 }) ); assert_eq!(layout.total_size(), 5); @@ -1650,15 +1650,15 @@ mod tests { #[test] fn union_nested_inside_struct_after_preceding_field() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "id": { "TypeDef:Uint8": true }, + "id": { "AlkType:Uint8": true }, "payload": { - "TypeDef:Union": true, + "AlkType:Union": true, "discriminator": { "kind": "byte", "offset": 0, - "type": "TypeDef:Uint8" + "type": "AlkType:Uint8" }, "mapping": { "5": { "$ref": "#/$defs/Read" } @@ -1667,9 +1667,9 @@ mod tests { }, "$defs": { "Read": { - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "handle": { "TypeDef:Uint32": true } + "handle": { "AlkType:Uint32": true } } } } @@ -1681,7 +1681,7 @@ mod tests { Some(&FieldPosition { offset: 0, size: 1, - kind: TypeDefKind::Uint8 + kind: AlkTypeKind::Uint8 }) ); assert_eq!( @@ -1689,7 +1689,7 @@ mod tests { Some(&FieldPosition { offset: 1, size: 1, - kind: TypeDefKind::Uint8 + kind: AlkTypeKind::Uint8 }) ); assert_eq!( @@ -1697,7 +1697,7 @@ mod tests { Some(&FieldPosition { offset: 2, size: 4, - kind: TypeDefKind::Uint32 + kind: AlkTypeKind::Uint32 }) ); assert_eq!(layout.total_size(), 6); diff --git a/src/lib.rs b/src/lib.rs index df513a7..c8c60f2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,13 +1,13 @@ //! alktype: The binary struct engine. //! -//! Takes a JSON Schema with `TypeDef:*` custom keywords and produces +//! Takes a JSON Schema with `AlkType:*` custom keywords and produces //! an offset map, read/write functions, and validation — all driven //! by the schema. The schema is the format definition; the engine is //! generic. //! //! ## Architecture //! -//! - **Schema layer** ([`schema`]): TypeDef kind detection, annotation +//! - **Schema layer** ([`schema`]): AlkType kind detection, annotation //! parsing, `$ref` normalization, endianness. //! - **Layout engine** ([`offset_map`], [`layout_builder`], //! [`sequential_reader`]): Two layout modes — aligned static for @@ -17,8 +17,8 @@ //! - **TUnion dispatch** ([`tunion`]): Byte-offset and field-name //! discriminator dispatch. //! - **Validation** ([`validation`]): Custom keyword validators for all -//! 17 `TypeDef:*` kinds, delegated to the `jsonschema` crate. -//! - **Engine** ([`engine`]): `TypedefEngine` — the compiled form of a +//! 17 `AlkType:*` kinds, delegated to the `jsonschema` crate. +//! - **Engine** ([`engine`]): `AlkTypeEngine` — the compiled form of a //! schema, combining layout and validation. #[macro_use] @@ -33,14 +33,14 @@ pub mod sequential_reader; pub mod tunion; pub mod validation; -pub use engine::{LayoutMode, TypedefEngine}; -pub use error::TypedefError; +pub use engine::{LayoutMode, AlkTypeEngine}; +pub use error::AlkTypeError; pub use layout_builder::{FieldPosition, LayoutBuilder, PackedLayout}; pub use offset_map::{ByteRange, OffsetMap}; pub use schema::{ - get_typedef_kind_loose, get_typedef_kind_loose_enum, normalize_refs, parse_align, + get_alktype_kind_loose, get_alktype_kind_loose_enum, normalize_refs, parse_align, parse_discriminator, parse_encoding, parse_endian, parse_max_length, resolve_ref, - resolve_ref_or_inline, DiscriminatorKind, Endian, TypeDefKind, VariableEncoding, + resolve_ref_or_inline, DiscriminatorKind, Endian, AlkTypeKind, VariableEncoding, }; pub use sequential_reader::{FieldValue, SequentialReader}; pub use tunion::UnionDispatch; diff --git a/src/macros.rs b/src/macros.rs index 0711d2d..11ae2e8 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1,4 +1,4 @@ -//! Macros for generating repetitive code across the 17 TypeDef kinds. +//! Macros for generating repetitive code across the 17 AlkType kinds. //! //! These macros eliminate boilerplate in validation, data access, and //! dispatch. Each macro takes a compact specification and generates the @@ -187,7 +187,7 @@ macro_rules! define_read_write_endian { offset: usize, field_path: &str, endian: $crate::Endian, - ) -> Result<$rust_ty, $crate::TypedefError> { + ) -> Result<$rust_ty, $crate::AlkTypeError> { let bytes: [u8; $size] = $crate::data_access::read_array(buffer, offset, field_path)?; Ok(match endian { $crate::Endian::Little => <$rust_ty>::from_le_bytes(bytes), @@ -206,7 +206,7 @@ macro_rules! define_read_write_endian { value: $rust_ty, field_path: &str, endian: $crate::Endian, - ) -> Result<(), $crate::TypedefError> { + ) -> Result<(), $crate::AlkTypeError> { let bytes = match endian { $crate::Endian::Little => value.to_le_bytes(), $crate::Endian::Big => value.to_be_bytes(), @@ -229,7 +229,7 @@ macro_rules! define_read_write_ne { buffer: &[u8], offset: usize, field_path: &str, - ) -> Result<$rust_ty, $crate::TypedefError> { + ) -> Result<$rust_ty, $crate::AlkTypeError> { let bytes: [u8; $size] = $crate::data_access::read_array(buffer, offset, field_path)?; Ok($read_expr(bytes)) } @@ -244,7 +244,7 @@ macro_rules! define_read_write_ne { offset: usize, value: $rust_ty, field_path: &str, - ) -> Result<(), $crate::TypedefError> { + ) -> Result<(), $crate::AlkTypeError> { $crate::data_access::write_array(buffer, offset, value.to_ne_bytes(), field_path) } }; diff --git a/src/offset_map.rs b/src/offset_map.rs index 0df45ea..f9a9b8c 100644 --- a/src/offset_map.rs +++ b/src/offset_map.rs @@ -11,10 +11,10 @@ //! to satisfy the field's alignment requirement (natural alignment by //! default, overridable via the `"align"` annotation). -use crate::error::TypedefError; +use crate::error::AlkTypeError; use crate::schema::{ - get_typedef_kind, get_typedef_kind_loose_enum, parse_align, parse_encoding, - parse_max_length, resolve_ref_or_inline, TypeDefKind, VariableEncoding, + get_alktype_kind, get_alktype_kind_loose_enum, parse_align, parse_encoding, + parse_max_length, resolve_ref_or_inline, AlkTypeKind, VariableEncoding, }; use serde_json::Value; @@ -67,25 +67,25 @@ impl OffsetMap { /// /// Walks the schema recursively, computing byte positions for each /// field based on type sizes, field order, and alignment. The - /// top-level schema must be a `TypeDef:Struct`. + /// top-level schema must be a `AlkType:Struct`. /// /// # Errors /// - /// Returns [`TypedefError::Schema`] if the top-level schema is not a - /// `TypeDef:Struct` or has no `TypeDef:*` kind, or if the schema is + /// Returns [`AlkTypeError::Schema`] if the top-level schema is not a + /// `AlkType:Struct` or has no `AlkType:*` kind, or if the schema is /// malformed (missing `properties`, unknown kind, etc.). /// - /// Returns [`TypedefError::Offset`] for unsupported type combinations + /// Returns [`AlkTypeError::Offset`] for unsupported type combinations /// encountered during the walk. - pub fn compute(schema: &Value) -> Result { - let kind = get_typedef_kind(schema) - .and_then(|s| s.parse::().ok()) + pub fn compute(schema: &Value) -> Result { + let kind = get_alktype_kind(schema) + .and_then(|s| s.parse::().ok()) .ok_or_else(|| { - TypedefError::Schema("top-level schema has no TypeDef:* kind".to_string()) + AlkTypeError::Schema("top-level schema has no AlkType:* kind".to_string()) })?; - if kind != TypeDefKind::Struct { - return Err(TypedefError::Schema(format!( - "OffsetMap::compute requires a TypeDef:Struct at the top level, got {kind}" + if kind != AlkTypeKind::Struct { + return Err(AlkTypeError::Schema(format!( + "OffsetMap::compute requires a AlkType:Struct at the top level, got {kind}" ))); } let mut ctx = ComputeCtx { @@ -145,7 +145,7 @@ struct FieldLayout { } impl<'a> ComputeCtx<'a> { - /// Recurse into a `TypeDef:Struct`, appending `(field_path, ByteRange)` + /// Recurse into a `AlkType:Struct`, appending `(field_path, ByteRange)` /// pairs to `self.fields` and advancing `self.offset`. /// /// Returns `(total_size, alignment)` where `total_size` includes @@ -163,15 +163,15 @@ impl<'a> ComputeCtx<'a> { struct_schema: &Value, prefix: &str, parent_struct_align: usize, - ) -> Result<(usize, usize), TypedefError> { + ) -> Result<(usize, usize), AlkTypeError> { let obj = struct_schema .as_object() - .ok_or_else(|| TypedefError::Schema("struct schema is not an object".to_string()))?; + .ok_or_else(|| AlkTypeError::Schema("struct schema is not an object".to_string()))?; let properties = obj .get("properties") .and_then(|v| v.as_object()) .ok_or_else(|| { - TypedefError::Schema("struct schema has no 'properties' object".to_string()) + AlkTypeError::Schema("struct schema has no 'properties' object".to_string()) })?; let struct_default_align = parse_align(struct_schema).unwrap_or(parent_struct_align); @@ -194,13 +194,13 @@ impl<'a> ComputeCtx<'a> { // data_access::write_string writes prefix+data inline — clobbering // subsequent fields. Only allowed as the last field in the struct. if i < field_count - 1 { - if let Some(kind) = get_typedef_kind_loose_enum(field_schema) { + if let Some(kind) = get_alktype_kind_loose_enum(field_schema) { if kind.is_variable_length() { let keyword_value = field_schema .as_object() .and_then(|o| { o.keys() - .find(|k| k.starts_with("TypeDef:")) + .find(|k| k.starts_with("AlkType:")) .and_then(|k| o.get(k)) }) .cloned() @@ -210,7 +210,7 @@ impl<'a> ComputeCtx<'a> { let is_inline_length_prefixed = encoding == VariableEncoding::LengthPrefixed && max_length.is_none(); if is_inline_length_prefixed { - return Err(TypedefError::Offset { + return Err(AlkTypeError::Offset { field_path: field_path.clone(), reason: format!( "non-final inline length-prefixed variable field \ @@ -244,17 +244,17 @@ impl<'a> ComputeCtx<'a> { field_schema: &Value, field_path: &str, struct_default_align: usize, - ) -> Result { - let kind = get_typedef_kind_loose_enum(field_schema).ok_or_else(|| TypedefError::Offset { + ) -> Result { + let kind = get_alktype_kind_loose_enum(field_schema).ok_or_else(|| AlkTypeError::Offset { field_path: field_path.to_string(), - reason: "field schema has no TypeDef:* kind".to_string(), + reason: "field schema has no AlkType:* kind".to_string(), })?; match kind { - TypeDefKind::Struct => { + AlkTypeKind::Struct => { self.compute_struct_field(field_schema, field_path, struct_default_align) } - TypeDefKind::Union => Err(TypedefError::Offset { + AlkTypeKind::Union => Err(AlkTypeError::Offset { field_path: field_path.to_string(), reason: "TUnion is not supported in aligned static mode (ADR-102). \ Unions are the protocol dispatch pattern — use packed sequential \ @@ -262,31 +262,31 @@ impl<'a> ComputeCtx<'a> { a struct with an explicit discriminator field." .to_string(), }), - TypeDefKind::Array => { + AlkTypeKind::Array => { self.compute_array_field(field_schema, field_path, struct_default_align) } - TypeDefKind::String - | TypeDefKind::Bytes - | TypeDefKind::Record - | TypeDefKind::Timestamp => { + AlkTypeKind::String + | AlkTypeKind::Bytes + | AlkTypeKind::Record + | AlkTypeKind::Timestamp => { self.compute_variable_field(field_schema, field_path, struct_default_align) } k if k.is_fixed_size() => { self.compute_fixed_field(k, field_schema, field_path, struct_default_align) } - _ => unreachable!("all TypeDefKind variants are covered above"), + _ => unreachable!("all AlkTypeKind variants are covered above"), } } /// Compute the layout for a fixed-size primitive field. fn compute_fixed_field( &mut self, - kind: TypeDefKind, + kind: AlkTypeKind, field_schema: &Value, field_path: &str, struct_default_align: usize, - ) -> Result { - let size = kind.type_size().ok_or_else(|| TypedefError::Offset { + ) -> Result { + let size = kind.type_size().ok_or_else(|| AlkTypeError::Offset { field_path: field_path.to_string(), reason: format!("type_size returned None for fixed kind {kind}"), })?; @@ -299,7 +299,7 @@ impl<'a> ComputeCtx<'a> { Ok(FieldLayout { align }) } - /// Compute the layout for a nested `TypeDef:Struct` field. + /// Compute the layout for a nested `AlkType:Struct` field. /// /// Probes the nested struct's layout at a temporary offset of 0 to /// determine its total size and alignment, aligns the parent offset, @@ -309,7 +309,7 @@ impl<'a> ComputeCtx<'a> { field_schema: &Value, field_path: &str, struct_default_align: usize, - ) -> Result { + ) -> Result { let inner_parent_align = parse_align(field_schema).unwrap_or(struct_default_align); let mut probe = ComputeCtx { root: self.root, @@ -335,37 +335,37 @@ impl<'a> ComputeCtx<'a> { Ok(FieldLayout { align }) } - /// Compute the layout for a `TypeDef:Array` field. + /// Compute the layout for a `AlkType:Array` field. fn compute_array_field( &mut self, field_schema: &Value, field_path: &str, struct_default_align: usize, - ) -> Result { + ) -> Result { let obj = field_schema .as_object() - .ok_or_else(|| TypedefError::Offset { + .ok_or_else(|| AlkTypeError::Offset { field_path: field_path.to_string(), reason: "array schema is not an object".to_string(), })?; - let items = obj.get("items").ok_or_else(|| TypedefError::Offset { + let items = obj.get("items").ok_or_else(|| AlkTypeError::Offset { field_path: field_path.to_string(), reason: "TArray is missing 'items'".to_string(), })?; let element_schema = - resolve_ref_or_inline(items, self.root).ok_or_else(|| TypedefError::Offset { + resolve_ref_or_inline(items, self.root).ok_or_else(|| AlkTypeError::Offset { field_path: field_path.to_string(), reason: "could not resolve TArray items schema".to_string(), })?; - let elem_kind = get_typedef_kind(element_schema) - .and_then(|s| s.parse::().ok()) - .ok_or_else(|| TypedefError::Offset { + let elem_kind = get_alktype_kind(element_schema) + .and_then(|s| s.parse::().ok()) + .ok_or_else(|| AlkTypeError::Offset { field_path: field_path.to_string(), - reason: "TArray element schema has no TypeDef:* kind".to_string(), + reason: "TArray element schema has no AlkType:* kind".to_string(), })?; if !elem_kind.is_fixed_size() { - return Err(TypedefError::Offset { + return Err(AlkTypeError::Offset { field_path: field_path.to_string(), reason: format!( "TArray of variable-length element kind {elem_kind} is not supported (OQ-069)" @@ -373,7 +373,7 @@ impl<'a> ComputeCtx<'a> { }); } - let elem_size = elem_kind.type_size().ok_or_else(|| TypedefError::Offset { + let elem_size = elem_kind.type_size().ok_or_else(|| AlkTypeError::Offset { field_path: field_path.to_string(), reason: format!("element kind {elem_kind} has no fixed size"), })?; @@ -431,12 +431,12 @@ impl<'a> ComputeCtx<'a> { field_schema: &Value, field_path: &str, struct_default_align: usize, - ) -> Result { + ) -> Result { let keyword_value = field_schema .as_object() .and_then(|o| { o.keys() - .find(|k| k.starts_with("TypeDef:")) + .find(|k| k.starts_with("AlkType:")) .and_then(|k| o.get(k)) }) .cloned() @@ -510,10 +510,10 @@ mod tests { #[test] fn simple_fixed_fields_natural_alignment() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "flag": { "TypeDef:Uint8": true }, - "id": { "TypeDef:Uint32": true } + "flag": { "AlkType:Uint8": true }, + "id": { "AlkType:Uint32": true } } }); let m = map(&schema); @@ -525,10 +525,10 @@ mod tests { #[test] fn u8_then_u32_three_bytes_padding() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "a": { "TypeDef:Uint8": true }, - "b": { "TypeDef:Uint32": true } + "a": { "AlkType:Uint8": true }, + "b": { "AlkType:Uint32": true } } }); let m = map(&schema); @@ -539,16 +539,16 @@ mod tests { #[test] fn nested_struct_dotted_paths() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { "header": { - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "magic": { "TypeDef:Uint32": true }, - "version": { "TypeDef:Uint8": true } + "magic": { "AlkType:Uint32": true }, + "version": { "AlkType:Uint8": true } } }, - "body": { "TypeDef:Uint32": true } + "body": { "AlkType:Uint32": true } } }); let m = map(&schema); @@ -564,11 +564,11 @@ mod tests { #[test] fn array_fixed_count_element_offsets() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { "vals": { - "TypeDef:Array": true, - "items": { "TypeDef:Uint32": true }, + "AlkType:Array": true, + "items": { "AlkType:Uint32": true }, "minItems": 3, "maxItems": 3 } @@ -584,11 +584,11 @@ mod tests { #[test] fn array_variable_count_length_prefix() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { "vals": { - "TypeDef:Array": true, - "items": { "TypeDef:Uint32": true } + "AlkType:Array": true, + "items": { "AlkType:Uint32": true } } } }); @@ -600,10 +600,10 @@ mod tests { #[test] fn variable_string_length_prefix_at_known_offset() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "id": { "TypeDef:Uint32": true }, - "name": { "TypeDef:String": true } + "id": { "AlkType:Uint32": true }, + "name": { "AlkType:String": true } } }); let m = map(&schema); @@ -615,10 +615,10 @@ mod tests { #[test] fn variable_string_max_length_reservation() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "id": { "TypeDef:Uint32": true }, - "name": { "TypeDef:String": true, "maxLength": 256 } + "id": { "AlkType:Uint32": true }, + "name": { "AlkType:String": true, "maxLength": 256 } } }); let m = map(&schema); @@ -630,10 +630,10 @@ mod tests { #[test] fn variable_string_offset_indirect_eight_bytes() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "id": { "TypeDef:Uint32": true }, - "blob": { "TypeDef:String": { "encoding": "offset-indirect" } } + "id": { "AlkType:Uint32": true }, + "blob": { "AlkType:String": { "encoding": "offset-indirect" } } } }); let m = map(&schema); @@ -645,14 +645,14 @@ mod tests { #[test] fn union_byte_discriminator_rejected_in_aligned_mode() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { "payload": { - "TypeDef:Union": true, + "AlkType:Union": true, "discriminator": { "kind": "byte", "offset": 0, - "type": "TypeDef:Uint8" + "type": "AlkType:Uint8" }, "mapping": { "5": { "$ref": "#/$defs/Read" }, @@ -662,26 +662,26 @@ mod tests { }, "$defs": { "Read": { - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "handle": { "TypeDef:Uint32": true }, - "length": { "TypeDef:Uint32": true } + "handle": { "AlkType:Uint32": true }, + "length": { "AlkType:Uint32": true } } }, "Write": { - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "handle": { "TypeDef:Uint32": true }, - "length": { "TypeDef:Uint32": true }, - "data": { "TypeDef:Uint32": true } + "handle": { "AlkType:Uint32": true }, + "length": { "AlkType:Uint32": true }, + "data": { "AlkType:Uint32": true } } } } }); let err = OffsetMap::compute(&schema).unwrap_err(); - assert!(matches!(err, TypedefError::Offset { .. }), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Offset { .. }), "got {err:?}"); let reason = match err { - TypedefError::Offset { reason, .. } => reason, + AlkTypeError::Offset { reason, .. } => reason, _ => unreachable!(), }; assert!(reason.contains("ADR-102"), "reason: {reason}"); @@ -690,10 +690,10 @@ mod tests { #[test] fn union_field_name_discriminator_rejected_in_aligned_mode() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { "event": { - "TypeDef:Union": true, + "AlkType:Union": true, "discriminator": { "kind": "field", "name": "type" }, "mapping": { "read": { "$ref": "#/$defs/Read" }, @@ -703,38 +703,38 @@ mod tests { }, "$defs": { "Read": { - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "type": { "TypeDef:Uint8": true }, - "handle": { "TypeDef:Uint32": true } + "type": { "AlkType:Uint8": true }, + "handle": { "AlkType:Uint32": true } } }, "Write": { - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "type": { "TypeDef:Uint8": true }, - "handle": { "TypeDef:Uint32": true }, - "length": { "TypeDef:Uint32": true } + "type": { "AlkType:Uint8": true }, + "handle": { "AlkType:Uint32": true }, + "length": { "AlkType:Uint32": true } } } } }); let err = OffsetMap::compute(&schema).unwrap_err(); - assert!(matches!(err, TypedefError::Offset { .. }), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Offset { .. }), "got {err:?}"); } #[test] fn non_final_inline_string_rejected_in_aligned_mode() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "name": { "TypeDef:String": true }, - "id": { "TypeDef:Uint32": true } + "name": { "AlkType:String": true }, + "id": { "AlkType:Uint32": true } } }); let err = OffsetMap::compute(&schema).unwrap_err(); match err { - TypedefError::Offset { field_path, reason } => { + AlkTypeError::Offset { field_path, reason } => { assert_eq!(field_path, "name"); assert!(reason.contains("ADR-100"), "reason: {reason}"); } @@ -745,10 +745,10 @@ mod tests { #[test] fn final_inline_string_allowed_in_aligned_mode() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "id": { "TypeDef:Uint32": true }, - "name": { "TypeDef:String": true } + "id": { "AlkType:Uint32": true }, + "name": { "AlkType:String": true } } }); let m = map(&schema); @@ -759,10 +759,10 @@ mod tests { #[test] fn non_final_maxlength_string_allowed_in_aligned_mode() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "name": { "TypeDef:String": true, "maxLength": 256 }, - "id": { "TypeDef:Uint32": true } + "name": { "AlkType:String": true, "maxLength": 256 }, + "id": { "AlkType:Uint32": true } } }); let m = map(&schema); @@ -773,10 +773,10 @@ mod tests { #[test] fn non_final_offset_indirect_string_allowed_in_aligned_mode() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "blob": { "TypeDef:String": { "encoding": "offset-indirect" } }, - "id": { "TypeDef:Uint32": true } + "blob": { "AlkType:String": { "encoding": "offset-indirect" } }, + "id": { "AlkType:Uint32": true } } }); let m = map(&schema); @@ -787,10 +787,10 @@ mod tests { #[test] fn struct_level_align_rounds_up_total() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "align": 16, "properties": { - "flag": { "TypeDef:Uint8": true } + "flag": { "AlkType:Uint8": true } } }); let m = map(&schema); @@ -801,12 +801,12 @@ mod tests { #[test] fn field_level_align_overrides_struct_default() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "align": 1, "properties": { - "tag": { "TypeDef:Uint8": true }, - "flag": { "TypeDef:Uint8": true, "align": 16 }, - "id": { "TypeDef:Uint32": true } + "tag": { "AlkType:Uint8": true }, + "flag": { "AlkType:Uint8": true, "align": 16 }, + "id": { "AlkType:Uint32": true } } }); let m = map(&schema); @@ -819,11 +819,11 @@ mod tests { #[test] fn field_align_smaller_than_struct_default() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "align": 8, "properties": { - "a": { "TypeDef:Uint8": true }, - "b": { "TypeDef:Uint32": true, "align": 1 } + "a": { "AlkType:Uint8": true }, + "b": { "AlkType:Uint32": true, "align": 1 } } }); let m = map(&schema); @@ -835,10 +835,10 @@ mod tests { #[test] fn iter_returns_all_paths_in_order() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "a": { "TypeDef:Uint8": true }, - "b": { "TypeDef:Uint32": true } + "a": { "AlkType:Uint8": true }, + "b": { "AlkType:Uint32": true } } }); let m = map(&schema); @@ -849,16 +849,16 @@ mod tests { #[test] fn compute_rejects_non_struct_top_level() { let schema = - json!({ "TypeDef:Union": true, "discriminator": { "kind": "byte" }, "mapping": {} }); + json!({ "AlkType:Union": true, "discriminator": { "kind": "byte" }, "mapping": {} }); let err = OffsetMap::compute(&schema).unwrap_err(); - assert!(matches!(err, TypedefError::Schema(_))); + assert!(matches!(err, AlkTypeError::Schema(_))); } #[test] - fn compute_rejects_missing_typedef_kind() { + fn compute_rejects_missing_alktype_kind() { let schema = json!({ "type": "object", "properties": {} }); let err = OffsetMap::compute(&schema).unwrap_err(); - assert!(matches!(err, TypedefError::Schema(_))); + assert!(matches!(err, AlkTypeError::Schema(_))); } #[test] diff --git a/src/schema.rs b/src/schema.rs index b64d01e..2996292 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -1,36 +1,36 @@ -//! Schema layer: TypeDef kind detection, annotation parsing, `$ref` +//! Schema layer: AlkType kind detection, annotation parsing, `$ref` //! normalization, and the `Endian` enum. //! //! Per ADR-097 and the schema-layer spec. This module provides the //! foundational types and functions that every other module depends on: -//! `TypeDef:*` kind detection, fixed byte-size lookups, natural alignment, +//! `AlkType:*` kind detection, fixed byte-size lookups, natural alignment, //! schema annotation parsing (`encoding`, `align`, `maxLength`, `endian`), //! TUnion discriminator parsing, and `$ref` normalization from TypeBox //! bare-name refs to JSON Pointer refs. -use crate::error::TypedefError; +use crate::error::AlkTypeError; use serde_json::Value; use std::fmt; use std::str::FromStr; -const TYPEDEF_PREFIX: &str = "TypeDef:"; +const ALKTYPE_PREFIX: &str = "AlkType:"; pub(crate) const U32_SIZE: usize = 4; pub(crate) const DISCRIMINATOR_PATH: &str = "__discriminator"; -const BYTE_DISCRIMINATOR_TYPES: &[TypeDefKind] = &[ - TypeDefKind::Uint8, - TypeDefKind::Uint16, - TypeDefKind::Uint32, +const BYTE_DISCRIMINATOR_TYPES: &[AlkTypeKind] = &[ + AlkTypeKind::Uint8, + AlkTypeKind::Uint16, + AlkTypeKind::Uint32, ]; -/// The 19 `TypeDef:*` kinds recognized by the engine. +/// The 19 `AlkType:*` kinds recognized by the engine. /// -/// Each variant corresponds to a `TypeDef:` JSON Schema keyword. +/// Each variant corresponds to a `AlkType:` JSON Schema keyword. /// The enum provides compile-time exhaustiveness checking and integer /// discriminant dispatch (jump table) instead of string comparison. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub enum TypeDefKind { +pub enum AlkTypeKind { Int8, Int16, Int32, @@ -52,48 +52,48 @@ pub enum TypeDefKind { Timestamp, } -impl TypeDefKind { - /// The JSON Schema keyword string, e.g. `"TypeDef:Int8"`. +impl AlkTypeKind { + /// The JSON Schema keyword string, e.g. `"AlkType:Int8"`. pub fn as_str(self) -> &'static str { match self { - TypeDefKind::Int8 => "TypeDef:Int8", - TypeDefKind::Int16 => "TypeDef:Int16", - TypeDefKind::Int32 => "TypeDef:Int32", - TypeDefKind::Int64 => "TypeDef:Int64", - TypeDefKind::Uint8 => "TypeDef:Uint8", - TypeDefKind::Uint16 => "TypeDef:Uint16", - TypeDefKind::Uint32 => "TypeDef:Uint32", - TypeDefKind::Uint64 => "TypeDef:Uint64", - TypeDefKind::Float32 => "TypeDef:Float32", - TypeDefKind::Float64 => "TypeDef:Float64", - TypeDefKind::Boolean => "TypeDef:Boolean", - TypeDefKind::Enum => "TypeDef:Enum", - TypeDefKind::String => "TypeDef:String", - TypeDefKind::Bytes => "TypeDef:Bytes", - TypeDefKind::Struct => "TypeDef:Struct", - TypeDefKind::Union => "TypeDef:Union", - TypeDefKind::Array => "TypeDef:Array", - TypeDefKind::Record => "TypeDef:Record", - TypeDefKind::Timestamp => "TypeDef:Timestamp", + AlkTypeKind::Int8 => "AlkType:Int8", + AlkTypeKind::Int16 => "AlkType:Int16", + AlkTypeKind::Int32 => "AlkType:Int32", + AlkTypeKind::Int64 => "AlkType:Int64", + AlkTypeKind::Uint8 => "AlkType:Uint8", + AlkTypeKind::Uint16 => "AlkType:Uint16", + AlkTypeKind::Uint32 => "AlkType:Uint32", + AlkTypeKind::Uint64 => "AlkType:Uint64", + AlkTypeKind::Float32 => "AlkType:Float32", + AlkTypeKind::Float64 => "AlkType:Float64", + AlkTypeKind::Boolean => "AlkType:Boolean", + AlkTypeKind::Enum => "AlkType:Enum", + AlkTypeKind::String => "AlkType:String", + AlkTypeKind::Bytes => "AlkType:Bytes", + AlkTypeKind::Struct => "AlkType:Struct", + AlkTypeKind::Union => "AlkType:Union", + AlkTypeKind::Array => "AlkType:Array", + AlkTypeKind::Record => "AlkType:Record", + AlkTypeKind::Timestamp => "AlkType:Timestamp", } } /// Fixed byte size, or `None` for variable-size / composite kinds. pub fn type_size(self) -> Option { match self { - TypeDefKind::Float32 | TypeDefKind::Int32 | TypeDefKind::Uint32 | TypeDefKind::Enum => { + AlkTypeKind::Float32 | AlkTypeKind::Int32 | AlkTypeKind::Uint32 | AlkTypeKind::Enum => { Some(4) } - TypeDefKind::Float64 | TypeDefKind::Int64 | TypeDefKind::Uint64 => Some(8), - TypeDefKind::Int8 | TypeDefKind::Uint8 | TypeDefKind::Boolean => Some(1), - TypeDefKind::Int16 | TypeDefKind::Uint16 => Some(2), - TypeDefKind::String - | TypeDefKind::Bytes - | TypeDefKind::Struct - | TypeDefKind::Union - | TypeDefKind::Array - | TypeDefKind::Record - | TypeDefKind::Timestamp => None, + AlkTypeKind::Float64 | AlkTypeKind::Int64 | AlkTypeKind::Uint64 => Some(8), + AlkTypeKind::Int8 | AlkTypeKind::Uint8 | AlkTypeKind::Boolean => Some(1), + AlkTypeKind::Int16 | AlkTypeKind::Uint16 => Some(2), + AlkTypeKind::String + | AlkTypeKind::Bytes + | AlkTypeKind::Struct + | AlkTypeKind::Union + | AlkTypeKind::Array + | AlkTypeKind::Record + | AlkTypeKind::Timestamp => None, } } @@ -102,18 +102,18 @@ impl TypeDefKind { /// composites. pub fn natural_alignment(self) -> usize { match self { - TypeDefKind::Int8 | TypeDefKind::Uint8 | TypeDefKind::Boolean => 1, - TypeDefKind::Int16 | TypeDefKind::Uint16 => 2, - TypeDefKind::Int32 - | TypeDefKind::Uint32 - | TypeDefKind::Float32 - | TypeDefKind::Enum => 4, - TypeDefKind::Float64 | TypeDefKind::Int64 | TypeDefKind::Uint64 => 8, - TypeDefKind::String - | TypeDefKind::Bytes - | TypeDefKind::Record - | TypeDefKind::Timestamp => 4, - TypeDefKind::Struct | TypeDefKind::Union | TypeDefKind::Array => 1, + AlkTypeKind::Int8 | AlkTypeKind::Uint8 | AlkTypeKind::Boolean => 1, + AlkTypeKind::Int16 | AlkTypeKind::Uint16 => 2, + AlkTypeKind::Int32 + | AlkTypeKind::Uint32 + | AlkTypeKind::Float32 + | AlkTypeKind::Enum => 4, + AlkTypeKind::Float64 | AlkTypeKind::Int64 | AlkTypeKind::Uint64 => 8, + AlkTypeKind::String + | AlkTypeKind::Bytes + | AlkTypeKind::Record + | AlkTypeKind::Timestamp => 4, + AlkTypeKind::Struct | AlkTypeKind::Union | AlkTypeKind::Array => 1, } } @@ -121,18 +121,18 @@ impl TypeDefKind { pub fn is_fixed_size(self) -> bool { matches!( self, - TypeDefKind::Float32 - | TypeDefKind::Float64 - | TypeDefKind::Int8 - | TypeDefKind::Int16 - | TypeDefKind::Int32 - | TypeDefKind::Int64 - | TypeDefKind::Uint8 - | TypeDefKind::Uint16 - | TypeDefKind::Uint32 - | TypeDefKind::Uint64 - | TypeDefKind::Boolean - | TypeDefKind::Enum + AlkTypeKind::Float32 + | AlkTypeKind::Float64 + | AlkTypeKind::Int8 + | AlkTypeKind::Int16 + | AlkTypeKind::Int32 + | AlkTypeKind::Int64 + | AlkTypeKind::Uint8 + | AlkTypeKind::Uint16 + | AlkTypeKind::Uint32 + | AlkTypeKind::Uint64 + | AlkTypeKind::Boolean + | AlkTypeKind::Enum ) } @@ -140,18 +140,18 @@ impl TypeDefKind { pub fn needs_endian(self) -> bool { matches!( self, - TypeDefKind::Int16 - | TypeDefKind::Int32 - | TypeDefKind::Int64 - | TypeDefKind::Uint16 - | TypeDefKind::Uint32 - | TypeDefKind::Uint64 - | TypeDefKind::Float32 - | TypeDefKind::Float64 - | TypeDefKind::Enum - | TypeDefKind::String - | TypeDefKind::Bytes - | TypeDefKind::Timestamp + AlkTypeKind::Int16 + | AlkTypeKind::Int32 + | AlkTypeKind::Int64 + | AlkTypeKind::Uint16 + | AlkTypeKind::Uint32 + | AlkTypeKind::Uint64 + | AlkTypeKind::Float32 + | AlkTypeKind::Float64 + | AlkTypeKind::Enum + | AlkTypeKind::String + | AlkTypeKind::Bytes + | AlkTypeKind::Timestamp ) } @@ -159,7 +159,7 @@ impl TypeDefKind { pub fn is_composite(self) -> bool { matches!( self, - TypeDefKind::Struct | TypeDefKind::Union | TypeDefKind::Array | TypeDefKind::Record + AlkTypeKind::Struct | AlkTypeKind::Union | AlkTypeKind::Array | AlkTypeKind::Record ) } @@ -167,72 +167,72 @@ impl TypeDefKind { pub fn is_variable_length(self) -> bool { matches!( self, - TypeDefKind::String - | TypeDefKind::Bytes - | TypeDefKind::Timestamp - | TypeDefKind::Record + AlkTypeKind::String + | AlkTypeKind::Bytes + | AlkTypeKind::Timestamp + | AlkTypeKind::Record ) } } -impl fmt::Display for TypeDefKind { +impl fmt::Display for AlkTypeKind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(self.as_str()) } } -impl FromStr for TypeDefKind { - type Err = TypedefError; +impl FromStr for AlkTypeKind { + type Err = AlkTypeError; fn from_str(s: &str) -> Result { match s { - "TypeDef:Int8" => Ok(TypeDefKind::Int8), - "TypeDef:Int16" => Ok(TypeDefKind::Int16), - "TypeDef:Int32" => Ok(TypeDefKind::Int32), - "TypeDef:Int64" => Ok(TypeDefKind::Int64), - "TypeDef:Uint8" => Ok(TypeDefKind::Uint8), - "TypeDef:Uint16" => Ok(TypeDefKind::Uint16), - "TypeDef:Uint32" => Ok(TypeDefKind::Uint32), - "TypeDef:Uint64" => Ok(TypeDefKind::Uint64), - "TypeDef:Float32" => Ok(TypeDefKind::Float32), - "TypeDef:Float64" => Ok(TypeDefKind::Float64), - "TypeDef:Boolean" => Ok(TypeDefKind::Boolean), - "TypeDef:Enum" => Ok(TypeDefKind::Enum), - "TypeDef:String" => Ok(TypeDefKind::String), - "TypeDef:Bytes" => Ok(TypeDefKind::Bytes), - "TypeDef:Struct" => Ok(TypeDefKind::Struct), - "TypeDef:Union" => Ok(TypeDefKind::Union), - "TypeDef:Array" => Ok(TypeDefKind::Array), - "TypeDef:Record" => Ok(TypeDefKind::Record), - "TypeDef:Timestamp" => Ok(TypeDefKind::Timestamp), - other => Err(TypedefError::Schema(format!( - "unknown TypeDef kind: {other}" + "AlkType:Int8" => Ok(AlkTypeKind::Int8), + "AlkType:Int16" => Ok(AlkTypeKind::Int16), + "AlkType:Int32" => Ok(AlkTypeKind::Int32), + "AlkType:Int64" => Ok(AlkTypeKind::Int64), + "AlkType:Uint8" => Ok(AlkTypeKind::Uint8), + "AlkType:Uint16" => Ok(AlkTypeKind::Uint16), + "AlkType:Uint32" => Ok(AlkTypeKind::Uint32), + "AlkType:Uint64" => Ok(AlkTypeKind::Uint64), + "AlkType:Float32" => Ok(AlkTypeKind::Float32), + "AlkType:Float64" => Ok(AlkTypeKind::Float64), + "AlkType:Boolean" => Ok(AlkTypeKind::Boolean), + "AlkType:Enum" => Ok(AlkTypeKind::Enum), + "AlkType:String" => Ok(AlkTypeKind::String), + "AlkType:Bytes" => Ok(AlkTypeKind::Bytes), + "AlkType:Struct" => Ok(AlkTypeKind::Struct), + "AlkType:Union" => Ok(AlkTypeKind::Union), + "AlkType:Array" => Ok(AlkTypeKind::Array), + "AlkType:Record" => Ok(AlkTypeKind::Record), + "AlkType:Timestamp" => Ok(AlkTypeKind::Timestamp), + other => Err(AlkTypeError::Schema(format!( + "unknown AlkType kind: {other}" ))), } } } -/// Returns the `TypeDef:*` kind string if the schema node declares one. -/// Returns `None` if the node has no `TypeDef:*` keyword. +/// Returns the `AlkType:*` kind string if the schema node declares one. +/// Returns `None` if the node has no `AlkType:*` keyword. /// -/// A TypeDef kind is recognized when the schema object has a key starting -/// with `TypeDef:` whose value is `true`. (Object form with annotations +/// A AlkType kind is recognized when the schema object has a key starting +/// with `AlkType:` whose value is `true`. (Object form with annotations /// like `{ "encoding": "..." }` is handled by the annotation parsers, not -/// here — `get_typedef_kind` only checks for the presence of the keyword.) -pub fn get_typedef_kind(node: &Value) -> Option<&str> { +/// here — `get_alktype_kind` only checks for the presence of the keyword.) +pub fn get_alktype_kind(node: &Value) -> Option<&str> { let obj = node.as_object()?; for key in obj.keys() { - if key.starts_with(TYPEDEF_PREFIX) && obj.get(key) == Some(&Value::Bool(true)) { + if key.starts_with(ALKTYPE_PREFIX) && obj.get(key) == Some(&Value::Bool(true)) { return Some(key.as_str()); } } None } -/// Returns the `TypeDefKind` enum variant if the schema node declares one -/// (boolean form only, like `get_typedef_kind`). -pub fn get_typedef_kind_enum(node: &Value) -> Option { - get_typedef_kind(node).and_then(|s| s.parse().ok()) +/// Returns the `AlkTypeKind` enum variant if the schema node declares one +/// (boolean form only, like `get_alktype_kind`). +pub fn get_alktype_kind_enum(node: &Value) -> Option { + get_alktype_kind(node).and_then(|s| s.parse().ok()) } /// Byte endianness for multi-byte integer and float fields. @@ -316,9 +316,9 @@ pub enum DiscriminatorKind { Byte { /// Byte position of the discriminator within the union's buffer. offset: usize, - /// The `TypeDef:*` kind of the discriminator (typically - /// `TypeDef:Uint8`). - disc_type: TypeDefKind, + /// The `AlkType:*` kind of the discriminator (typically + /// `AlkType:Uint8`). + disc_type: AlkTypeKind, }, /// Field-name discriminator: a named field within the struct. Mapping keys /// are string values matching the discriminator field's value. The @@ -331,36 +331,36 @@ pub enum DiscriminatorKind { /// Parse the `"discriminator"` annotation from a TUnion schema node. /// -/// Returns [`TypedefError::Schema`] for malformed discriminators (unknown +/// Returns [`AlkTypeError::Schema`] for malformed discriminators (unknown /// `kind`, missing required `name`, or an unsupported discriminator `type`). -pub fn parse_discriminator(node: &Value) -> Result { +pub fn parse_discriminator(node: &Value) -> Result { let obj = node.as_object().ok_or_else(|| { - TypedefError::Schema("discriminator requires a schema object".to_string()) + AlkTypeError::Schema("discriminator requires a schema object".to_string()) })?; let disc = obj.get("discriminator").ok_or_else(|| { - TypedefError::Schema("union is missing 'discriminator' annotation".to_string()) + AlkTypeError::Schema("union is missing 'discriminator' annotation".to_string()) })?; let disc_obj = disc .as_object() - .ok_or_else(|| TypedefError::Schema("'discriminator' must be an object".to_string()))?; + .ok_or_else(|| AlkTypeError::Schema("'discriminator' must be an object".to_string()))?; let kind = disc_obj .get("kind") .and_then(Value::as_str) - .ok_or_else(|| TypedefError::Schema("discriminator is missing 'kind' field".to_string()))?; + .ok_or_else(|| AlkTypeError::Schema("discriminator is missing 'kind' field".to_string()))?; match kind { "byte" => { let offset = disc_obj.get("offset").and_then(Value::as_u64).unwrap_or(0) as usize; let disc_type_str = disc_obj .get("type") .and_then(Value::as_str) - .unwrap_or("TypeDef:Uint8"); - let disc_type: TypeDefKind = disc_type_str.parse().map_err(|_| { - TypedefError::Schema(format!( + .unwrap_or("AlkType:Uint8"); + let disc_type: AlkTypeKind = disc_type_str.parse().map_err(|_| { + AlkTypeError::Schema(format!( "discriminator 'type' must be one of {BYTE_DISCRIMINATOR_TYPES:?}, got {disc_type_str:?}" )) })?; if !BYTE_DISCRIMINATOR_TYPES.contains(&disc_type) { - return Err(TypedefError::Schema(format!( + return Err(AlkTypeError::Schema(format!( "discriminator 'type' must be one of {BYTE_DISCRIMINATOR_TYPES:?}, got {disc_type:?}" ))); } @@ -374,39 +374,39 @@ pub fn parse_discriminator(node: &Value) -> Result Err(TypedefError::Schema(format!( + other => Err(AlkTypeError::Schema(format!( "unknown discriminator 'kind': {other:?} (expected \"byte\" or \"field\")" ))), } } -/// Detect a `TypeDef:*` kind from a schema node, accepting either the -/// boolean form (`{ "TypeDef:String": true }`) or the object-annotation -/// form (`{ "TypeDef:String": { "encoding": "..." } }`). +/// Detect a `AlkType:*` kind from a schema node, accepting either the +/// boolean form (`{ "AlkType:String": true }`) or the object-annotation +/// form (`{ "AlkType:String": { "encoding": "..." } }`). /// -/// [`get_typedef_kind`] only recognizes the boolean form; layout computation +/// [`get_alktype_kind`] only recognizes the boolean form; layout computation /// and engine dispatch also need to recognize the object form so that /// variable-length encoding annotations don't hide the kind. -pub fn get_typedef_kind_loose(node: &Value) -> Option<&str> { +pub fn get_alktype_kind_loose(node: &Value) -> Option<&str> { let obj = node.as_object()?; for key in obj.keys() { - if key.starts_with(TYPEDEF_PREFIX) && obj.get(key).is_some_and(|v| !v.is_null()) { + if key.starts_with(ALKTYPE_PREFIX) && obj.get(key).is_some_and(|v| !v.is_null()) { return Some(key.as_str()); } } None } -/// Like [`get_typedef_kind_loose`] but returns the parsed [`TypeDefKind`] enum. -pub fn get_typedef_kind_loose_enum(node: &Value) -> Option { - get_typedef_kind_loose(node).and_then(|s| s.parse().ok()) +/// Like [`get_alktype_kind_loose`] but returns the parsed [`AlkTypeKind`] enum. +pub fn get_alktype_kind_loose_enum(node: &Value) -> Option { + get_alktype_kind_loose(node).and_then(|s| s.parse().ok()) } /// Resolve a `$ref` against the root schema, or return the inline schema. @@ -473,49 +473,49 @@ mod tests { use serde_json::json; #[test] - fn get_typedef_kind_detects_bool_keyword() { - let schema = json!({"TypeDef:Uint32": true}); - assert_eq!(get_typedef_kind(&schema), Some("TypeDef:Uint32")); + fn get_alktype_kind_detects_bool_keyword() { + let schema = json!({"AlkType:Uint32": true}); + assert_eq!(get_alktype_kind(&schema), Some("AlkType:Uint32")); } #[test] - fn get_typedef_kind_ignores_object_keyword() { - let schema = json!({"TypeDef:String": {"encoding": "length-prefixed"}}); - assert_eq!(get_typedef_kind(&schema), None); + fn get_alktype_kind_ignores_object_keyword() { + let schema = json!({"AlkType:String": {"encoding": "length-prefixed"}}); + assert_eq!(get_alktype_kind(&schema), None); } #[test] - fn get_typedef_kind_none_for_plain_schema() { + fn get_alktype_kind_none_for_plain_schema() { let schema = json!({"type": "object", "properties": {}}); - assert_eq!(get_typedef_kind(&schema), None); + assert_eq!(get_alktype_kind(&schema), None); } #[test] fn type_size_fixed_kinds() { - assert_eq!(TypeDefKind::Float32.type_size(), Some(4)); - assert_eq!(TypeDefKind::Float64.type_size(), Some(8)); - assert_eq!(TypeDefKind::Int8.type_size(), Some(1)); - assert_eq!(TypeDefKind::Int16.type_size(), Some(2)); - assert_eq!(TypeDefKind::Int32.type_size(), Some(4)); - assert_eq!(TypeDefKind::Int64.type_size(), Some(8)); - assert_eq!(TypeDefKind::Uint8.type_size(), Some(1)); - assert_eq!(TypeDefKind::Uint16.type_size(), Some(2)); - assert_eq!(TypeDefKind::Uint32.type_size(), Some(4)); - assert_eq!(TypeDefKind::Uint64.type_size(), Some(8)); - assert_eq!(TypeDefKind::Boolean.type_size(), Some(1)); - assert_eq!(TypeDefKind::Enum.type_size(), Some(4)); + assert_eq!(AlkTypeKind::Float32.type_size(), Some(4)); + assert_eq!(AlkTypeKind::Float64.type_size(), Some(8)); + assert_eq!(AlkTypeKind::Int8.type_size(), Some(1)); + assert_eq!(AlkTypeKind::Int16.type_size(), Some(2)); + assert_eq!(AlkTypeKind::Int32.type_size(), Some(4)); + assert_eq!(AlkTypeKind::Int64.type_size(), Some(8)); + assert_eq!(AlkTypeKind::Uint8.type_size(), Some(1)); + assert_eq!(AlkTypeKind::Uint16.type_size(), Some(2)); + assert_eq!(AlkTypeKind::Uint32.type_size(), Some(4)); + assert_eq!(AlkTypeKind::Uint64.type_size(), Some(8)); + assert_eq!(AlkTypeKind::Boolean.type_size(), Some(1)); + assert_eq!(AlkTypeKind::Enum.type_size(), Some(4)); } #[test] fn type_size_variable_and_composite_kinds() { for kind in [ - TypeDefKind::String, - TypeDefKind::Bytes, - TypeDefKind::Struct, - TypeDefKind::Union, - TypeDefKind::Array, - TypeDefKind::Record, - TypeDefKind::Timestamp, + AlkTypeKind::String, + AlkTypeKind::Bytes, + AlkTypeKind::Struct, + AlkTypeKind::Union, + AlkTypeKind::Array, + AlkTypeKind::Record, + AlkTypeKind::Timestamp, ] { assert_eq!(kind.type_size(), None, "failed for {kind}"); } @@ -523,60 +523,60 @@ mod tests { #[test] fn type_size_unknown_kind_returns_none() { - assert!("TypeDef:Uint128".parse::().is_err()); - assert!("TypeDef:Int128".parse::().is_err()); - assert!("not-a-typedef".parse::().is_err()); + assert!("AlkType:Uint128".parse::().is_err()); + assert!("AlkType:Int128".parse::().is_err()); + assert!("not-an-alktype".parse::().is_err()); } #[test] fn natural_alignment_matches_spec() { - assert_eq!(TypeDefKind::Int8.natural_alignment(), 1); - assert_eq!(TypeDefKind::Uint8.natural_alignment(), 1); - assert_eq!(TypeDefKind::Boolean.natural_alignment(), 1); - assert_eq!(TypeDefKind::Int16.natural_alignment(), 2); - assert_eq!(TypeDefKind::Uint16.natural_alignment(), 2); - assert_eq!(TypeDefKind::Int32.natural_alignment(), 4); - assert_eq!(TypeDefKind::Uint32.natural_alignment(), 4); - assert_eq!(TypeDefKind::Float32.natural_alignment(), 4); - assert_eq!(TypeDefKind::Enum.natural_alignment(), 4); - assert_eq!(TypeDefKind::Float64.natural_alignment(), 8); - assert_eq!(TypeDefKind::Int64.natural_alignment(), 8); - assert_eq!(TypeDefKind::Uint64.natural_alignment(), 8); - assert_eq!(TypeDefKind::String.natural_alignment(), 4); - assert_eq!(TypeDefKind::Bytes.natural_alignment(), 4); - assert_eq!(TypeDefKind::Record.natural_alignment(), 4); - assert_eq!(TypeDefKind::Timestamp.natural_alignment(), 4); - assert_eq!(TypeDefKind::Struct.natural_alignment(), 1); - assert_eq!(TypeDefKind::Union.natural_alignment(), 1); - assert_eq!(TypeDefKind::Array.natural_alignment(), 1); + assert_eq!(AlkTypeKind::Int8.natural_alignment(), 1); + assert_eq!(AlkTypeKind::Uint8.natural_alignment(), 1); + assert_eq!(AlkTypeKind::Boolean.natural_alignment(), 1); + assert_eq!(AlkTypeKind::Int16.natural_alignment(), 2); + assert_eq!(AlkTypeKind::Uint16.natural_alignment(), 2); + assert_eq!(AlkTypeKind::Int32.natural_alignment(), 4); + assert_eq!(AlkTypeKind::Uint32.natural_alignment(), 4); + assert_eq!(AlkTypeKind::Float32.natural_alignment(), 4); + assert_eq!(AlkTypeKind::Enum.natural_alignment(), 4); + assert_eq!(AlkTypeKind::Float64.natural_alignment(), 8); + assert_eq!(AlkTypeKind::Int64.natural_alignment(), 8); + assert_eq!(AlkTypeKind::Uint64.natural_alignment(), 8); + assert_eq!(AlkTypeKind::String.natural_alignment(), 4); + assert_eq!(AlkTypeKind::Bytes.natural_alignment(), 4); + assert_eq!(AlkTypeKind::Record.natural_alignment(), 4); + assert_eq!(AlkTypeKind::Timestamp.natural_alignment(), 4); + assert_eq!(AlkTypeKind::Struct.natural_alignment(), 1); + assert_eq!(AlkTypeKind::Union.natural_alignment(), 1); + assert_eq!(AlkTypeKind::Array.natural_alignment(), 1); } #[test] fn is_fixed_size_classifies_correctly() { for kind in [ - TypeDefKind::Float32, - TypeDefKind::Float64, - TypeDefKind::Int8, - TypeDefKind::Int16, - TypeDefKind::Int32, - TypeDefKind::Int64, - TypeDefKind::Uint8, - TypeDefKind::Uint16, - TypeDefKind::Uint32, - TypeDefKind::Uint64, - TypeDefKind::Boolean, - TypeDefKind::Enum, + AlkTypeKind::Float32, + AlkTypeKind::Float64, + AlkTypeKind::Int8, + AlkTypeKind::Int16, + AlkTypeKind::Int32, + AlkTypeKind::Int64, + AlkTypeKind::Uint8, + AlkTypeKind::Uint16, + AlkTypeKind::Uint32, + AlkTypeKind::Uint64, + AlkTypeKind::Boolean, + AlkTypeKind::Enum, ] { assert!(kind.is_fixed_size(), "expected fixed: {kind}"); } for kind in [ - TypeDefKind::String, - TypeDefKind::Bytes, - TypeDefKind::Struct, - TypeDefKind::Union, - TypeDefKind::Array, - TypeDefKind::Record, - TypeDefKind::Timestamp, + AlkTypeKind::String, + AlkTypeKind::Bytes, + AlkTypeKind::Struct, + AlkTypeKind::Union, + AlkTypeKind::Array, + AlkTypeKind::Record, + AlkTypeKind::Timestamp, ] { assert!(!kind.is_fixed_size(), "expected variable: {kind}"); } @@ -674,7 +674,7 @@ mod tests { disc, DiscriminatorKind::Byte { offset: 0, - disc_type: TypeDefKind::Uint8, + disc_type: AlkTypeKind::Uint8, } ); } @@ -682,14 +682,14 @@ mod tests { #[test] fn parse_discriminator_byte_explicit() { let schema = json!({ - "discriminator": {"kind": "byte", "offset": 4, "type": "TypeDef:Uint16"} + "discriminator": {"kind": "byte", "offset": 4, "type": "AlkType:Uint16"} }); let disc = parse_discriminator(&schema).expect("byte discriminator"); assert_eq!( disc, DiscriminatorKind::Byte { offset: 4, - disc_type: TypeDefKind::Uint16, + disc_type: AlkTypeKind::Uint16, } ); } @@ -708,10 +708,10 @@ mod tests { #[test] fn parse_discriminator_missing_discriminator_is_error() { - let schema = json!({"TypeDef:Union": true}); + let schema = json!({"AlkType:Union": true}); assert!(matches!( parse_discriminator(&schema), - Err(TypedefError::Schema(_)) + Err(AlkTypeError::Schema(_)) )); } @@ -720,7 +720,7 @@ mod tests { let schema = json!({"discriminator": {"kind": "field"}}); assert!(matches!( parse_discriminator(&schema), - Err(TypedefError::Schema(_)) + Err(AlkTypeError::Schema(_)) )); } @@ -729,18 +729,18 @@ mod tests { let schema = json!({"discriminator": {"kind": "magic"}}); assert!(matches!( parse_discriminator(&schema), - Err(TypedefError::Schema(_)) + Err(AlkTypeError::Schema(_)) )); } #[test] fn parse_discriminator_byte_invalid_type_is_error() { let schema = json!({ - "discriminator": {"kind": "byte", "type": "TypeDef:Float32"} + "discriminator": {"kind": "byte", "type": "AlkType:Float32"} }); assert!(matches!( parse_discriminator(&schema), - Err(TypedefError::Schema(_)) + Err(AlkTypeError::Schema(_)) )); } @@ -798,14 +798,14 @@ mod tests { fn normalize_refs_preserves_sibling_keys() { let mut schema = json!({ "$ref": "Read", - "typedef:annotation": "kept" + "alktype:annotation": "kept" }); normalize_refs(&mut schema); assert_eq!( schema, json!({ "$ref": "#/$defs/Read", - "typedef:annotation": "kept" + "alktype:annotation": "kept" }) ); } diff --git a/src/sequential_reader.rs b/src/sequential_reader.rs index 8340bb3..fc95cb7 100644 --- a/src/sequential_reader.rs +++ b/src/sequential_reader.rs @@ -11,8 +11,8 @@ //! and applies the schema's endianness to every multi-byte value. use crate::data_access; -use crate::error::TypedefError; -use crate::schema::{self, get_typedef_kind_loose_enum, DiscriminatorKind, Endian, TypeDefKind, U32_SIZE}; +use crate::error::AlkTypeError; +use crate::schema::{self, get_alktype_kind_loose_enum, DiscriminatorKind, Endian, AlkTypeKind, U32_SIZE}; use serde_json::Value; /// A value read from a field during sequential traversal. @@ -23,35 +23,35 @@ use serde_json::Value; /// reported byte range. #[derive(Debug, PartialEq)] pub enum FieldValue<'a> { - /// `TypeDef:Int8`. + /// `AlkType:Int8`. I8(i8), - /// `TypeDef:Int16`. + /// `AlkType:Int16`. I16(i16), - /// `TypeDef:Int32`. + /// `AlkType:Int32`. I32(i32), - /// `TypeDef:Int64`. + /// `AlkType:Int64`. I64(i64), - /// `TypeDef:Uint8`. + /// `AlkType:Uint8`. U8(u8), - /// `TypeDef:Uint16`. + /// `AlkType:Uint16`. U16(u16), - /// `TypeDef:Uint32`. + /// `AlkType:Uint32`. U32(u32), - /// `TypeDef:Uint64`. + /// `AlkType:Uint64`. U64(u64), - /// `TypeDef:Float32`. + /// `AlkType:Float32`. F32(f32), - /// `TypeDef:Float64`. + /// `AlkType:Float64`. F64(f64), - /// `TypeDef:Boolean`. + /// `AlkType:Boolean`. Bool(bool), - /// `TypeDef:Enum` — `u32` index into the schema's `"enum"` array. + /// `AlkType:Enum` — `u32` index into the schema's `"enum"` array. Enum(u32), - /// `TypeDef:String` — borrows from the input buffer. + /// `AlkType:String` — borrows from the input buffer. String(&'a str), - /// `TypeDef:Bytes` — borrows from the input buffer. + /// `AlkType:Bytes` — borrows from the input buffer. Bytes(&'a [u8]), - /// `TypeDef:Struct` — the consumer recurses with a new + /// `AlkType:Struct` — the consumer recurses with a new /// [`SequentialReader`] scoped to `start..end`. Struct { /// Inclusive start of the nested struct's byte range. @@ -59,7 +59,7 @@ pub enum FieldValue<'a> { /// Exclusive end of the nested struct's byte range. end: usize, }, - /// `TypeDef:Union` — the consumer looks up the variant schema using + /// `AlkType:Union` — the consumer looks up the variant schema using /// `discriminator` and recurses at `variant_start`. Union { /// Stringified discriminator value (mapping key). @@ -67,7 +67,7 @@ pub enum FieldValue<'a> { /// Byte offset where the variant struct begins. variant_start: usize, }, - /// `TypeDef:Array` — the consumer iterates `count` elements of + /// `AlkType:Array` — the consumer iterates `count` elements of /// stride `element_stride` starting at `element_start`. Array { /// Number of elements in the array. @@ -106,20 +106,20 @@ pub struct SequentialReader { impl SequentialReader { /// Create a new `SequentialReader` from a top-level struct schema. /// - /// The schema must declare `TypeDef:Struct` and have a `properties` + /// The schema must declare `AlkType:Struct` and have a `properties` /// object. Endianness is parsed via [`Endian::from_schema`]. /// /// # Errors /// - /// Returns [`TypedefError::Schema`] if the schema is not an object, - /// does not declare `TypeDef:Struct`, or has no `properties` object. - pub fn new(schema: &Value) -> Result { - let kind = schema::get_typedef_kind(schema) - .and_then(|s| s.parse::().ok()) - .ok_or_else(|| TypedefError::Schema("schema has no TypeDef:* kind".to_string()))?; - if kind != TypeDefKind::Struct { - return Err(TypedefError::Schema(format!( - "SequentialReader only supports TypeDef:Struct at the top level, got {kind}" + /// Returns [`AlkTypeError::Schema`] if the schema is not an object, + /// does not declare `AlkType:Struct`, or has no `properties` object. + pub fn new(schema: &Value) -> Result { + let kind = schema::get_alktype_kind(schema) + .and_then(|s| s.parse::().ok()) + .ok_or_else(|| AlkTypeError::Schema("schema has no AlkType:* kind".to_string()))?; + if kind != AlkTypeKind::Struct { + return Err(AlkTypeError::Schema(format!( + "SequentialReader only supports AlkType:Struct at the top level, got {kind}" ))); } let properties = schema @@ -127,7 +127,7 @@ impl SequentialReader { .and_then(|obj| obj.get("properties")) .and_then(Value::as_object) .ok_or_else(|| { - TypedefError::Schema("struct schema has no properties object".to_string()) + AlkTypeError::Schema("struct schema has no properties object".to_string()) })?; let fields: Vec<(String, Value)> = properties .iter() @@ -147,19 +147,19 @@ impl SequentialReader { /// /// Returns `Ok(Some((field_name, value)))` and advances the internal /// position, or `Ok(None)` when all fields have been read. Variable- - /// length fields (`TypeDef:String`/`TypeDef:Bytes`) consume their + /// length fields (`AlkType:String`/`AlkType:Bytes`) consume their /// 4-byte length prefix plus the data; composite fields advance past /// their computed byte range. /// /// # Errors /// - /// Propagates [`TypedefError::Access`] from the underlying + /// Propagates [`AlkTypeError::Access`] from the underlying /// [`crate::data_access`] reads when `buffer` is too short or /// contains invalid data. pub fn read_next<'a>( &mut self, buffer: &'a [u8], - ) -> Result)>, TypedefError> { + ) -> Result)>, AlkTypeError> { if self.field_index >= self.fields.len() { return Ok(None); } @@ -181,21 +181,21 @@ impl SequentialReader { /// /// # Errors /// - /// Returns [`TypedefError::Schema`] if `field_path` does not match - /// any top-level field. Propagates [`TypedefError::Access`] for + /// Returns [`AlkTypeError::Schema`] if `field_path` does not match + /// any top-level field. Propagates [`AlkTypeError::Access`] for /// buffer-too-short or invalid data. pub fn read_field<'a>( &mut self, buffer: &'a [u8], field_path: &str, - ) -> Result, TypedefError> { + ) -> Result, AlkTypeError> { self.reset(); let target_index = self .fields .iter() .position(|(name, _)| name == field_path) .ok_or_else(|| { - TypedefError::Schema(format!("field not found in struct: {field_path}")) + AlkTypeError::Schema(format!("field not found in struct: {field_path}")) })?; let mut position = 0usize; let mut target_value: Option> = None; @@ -209,7 +209,7 @@ impl SequentialReader { self.position = position; self.field_index = target_index + 1; target_value.ok_or_else(|| { - TypedefError::Schema(format!( + AlkTypeError::Schema(format!( "internal: target field {field_path} not produced by loop" )) }) @@ -242,11 +242,11 @@ impl SequentialReader { buffer: &'a [u8], index: usize, offset: usize, - ) -> Result<(FieldValue<'a>, usize), TypedefError> { + ) -> Result<(FieldValue<'a>, usize), AlkTypeError> { let (_, field_schema) = self .fields .get(index) - .ok_or_else(|| TypedefError::Schema(format!("field index {index} out of range")))?; + .ok_or_else(|| AlkTypeError::Schema(format!("field index {index} out of range")))?; let field_path = self.fields[index].0.as_str(); read_field_value( buffer, @@ -272,88 +272,88 @@ fn read_field_value<'a>( field_path: &str, offset: usize, endian: Endian, -) -> Result<(FieldValue<'a>, usize), TypedefError> { - let kind = get_typedef_kind_loose_enum(field_schema).ok_or_else(|| { - TypedefError::Schema(format!( - "field {field_path} has no TypeDef:* kind: {field_schema}" +) -> Result<(FieldValue<'a>, usize), AlkTypeError> { + let kind = get_alktype_kind_loose_enum(field_schema).ok_or_else(|| { + AlkTypeError::Schema(format!( + "field {field_path} has no AlkType:* kind: {field_schema}" )) })?; match kind { - TypeDefKind::Int8 => { + AlkTypeKind::Int8 => { let v = data_access::read_i8(buffer, offset, field_path)?; Ok((FieldValue::I8(v), offset + 1)) } - TypeDefKind::Int16 => { + AlkTypeKind::Int16 => { let v = data_access::read_i16(buffer, offset, field_path, endian)?; Ok((FieldValue::I16(v), offset + 2)) } - TypeDefKind::Int32 => { + AlkTypeKind::Int32 => { let v = data_access::read_i32(buffer, offset, field_path, endian)?; Ok((FieldValue::I32(v), offset + 4)) } - TypeDefKind::Int64 => { + AlkTypeKind::Int64 => { let v = data_access::read_i64(buffer, offset, field_path, endian)?; Ok((FieldValue::I64(v), offset + 8)) } - TypeDefKind::Uint8 => { + AlkTypeKind::Uint8 => { let v = data_access::read_u8(buffer, offset, field_path)?; Ok((FieldValue::U8(v), offset + 1)) } - TypeDefKind::Uint16 => { + AlkTypeKind::Uint16 => { let v = data_access::read_u16(buffer, offset, field_path, endian)?; Ok((FieldValue::U16(v), offset + 2)) } - TypeDefKind::Uint32 => { + AlkTypeKind::Uint32 => { let v = data_access::read_u32(buffer, offset, field_path, endian)?; Ok((FieldValue::U32(v), offset + 4)) } - TypeDefKind::Uint64 => { + AlkTypeKind::Uint64 => { let v = data_access::read_u64(buffer, offset, field_path, endian)?; Ok((FieldValue::U64(v), offset + 8)) } - TypeDefKind::Float32 => { + AlkTypeKind::Float32 => { let v = data_access::read_f32(buffer, offset, field_path, endian)?; Ok((FieldValue::F32(v), offset + 4)) } - TypeDefKind::Float64 => { + AlkTypeKind::Float64 => { let v = data_access::read_f64(buffer, offset, field_path, endian)?; Ok((FieldValue::F64(v), offset + 8)) } - TypeDefKind::Boolean => { + AlkTypeKind::Boolean => { let v = data_access::read_bool(buffer, offset, field_path)?; Ok((FieldValue::Bool(v), offset + 1)) } - TypeDefKind::Enum => { + AlkTypeKind::Enum => { let v = data_access::read_enum(buffer, offset, field_path, endian)?; Ok((FieldValue::Enum(v), offset + 4)) } - TypeDefKind::String => { + AlkTypeKind::String => { let s = data_access::read_string(buffer, offset, field_path, endian)?; let total = U32_SIZE + s.len(); Ok((FieldValue::String(s), offset + total)) } - TypeDefKind::Bytes => { + AlkTypeKind::Bytes => { let b = data_access::read_bytes(buffer, offset, field_path, endian)?; let total = U32_SIZE + b.len(); Ok((FieldValue::Bytes(b), offset + total)) } - TypeDefKind::Timestamp => { + AlkTypeKind::Timestamp => { let s = data_access::read_string(buffer, offset, field_path, endian)?; let total = U32_SIZE + s.len(); Ok((FieldValue::String(s), offset + total)) } - TypeDefKind::Struct => { + AlkTypeKind::Struct => { let size = walk_struct_size(root_schema, field_schema, buffer, offset, endian)?; let end = offset .checked_add(size) - .ok_or_else(|| TypedefError::Access { + .ok_or_else(|| AlkTypeError::Access { field_path: field_path.to_string(), reason: format!("struct end {offset} + {size} overflows usize"), })?; Ok((FieldValue::Struct { start: offset, end }, end)) } - TypeDefKind::Union => read_union_value( + AlkTypeKind::Union => read_union_value( buffer, root_schema, field_schema, @@ -361,7 +361,7 @@ fn read_field_value<'a>( offset, endian, ), - TypeDefKind::Array => read_array_value( + AlkTypeKind::Array => read_array_value( buffer, root_schema, field_schema, @@ -369,7 +369,7 @@ fn read_field_value<'a>( offset, endian, ), - TypeDefKind::Record => read_record_value( + AlkTypeKind::Record => read_record_value( buffer, root_schema, field_schema, @@ -380,7 +380,7 @@ fn read_field_value<'a>( } } -/// Read a `TypeDef:Union` field: read the discriminator, return the +/// Read a `AlkType:Union` field: read the discriminator, return the /// variant start offset, and advance past the entire union payload. /// /// For byte-offset discriminators the union occupies @@ -400,13 +400,13 @@ fn read_union_value<'a>( field_path: &str, offset: usize, endian: Endian, -) -> Result<(FieldValue<'a>, usize), TypedefError> { +) -> Result<(FieldValue<'a>, usize), AlkTypeError> { let disc = schema::parse_discriminator(schema)?; let mapping = schema .as_object() .and_then(|obj| obj.get("mapping")) .and_then(Value::as_object) - .ok_or_else(|| TypedefError::Schema(format!("union {field_path} has no mapping object")))?; + .ok_or_else(|| AlkTypeError::Schema(format!("union {field_path} has no mapping object")))?; match disc { DiscriminatorKind::Byte { @@ -416,7 +416,7 @@ fn read_union_value<'a>( let abs_offset = offset .checked_add(disc_offset) - .ok_or_else(|| TypedefError::Access { + .ok_or_else(|| AlkTypeError::Access { field_path: field_path.to_string(), reason: format!( "discriminator offset {offset} + {disc_offset} overflows usize" @@ -425,14 +425,14 @@ fn read_union_value<'a>( let (disc_value, disc_size) = read_byte_discriminator(buffer, abs_offset, field_path, disc_type, endian)?; let key = disc_value.to_string(); - let variant_schema = mapping.get(&key).ok_or_else(|| TypedefError::Access { + let variant_schema = mapping.get(&key).ok_or_else(|| AlkTypeError::Access { field_path: field_path.to_string(), reason: format!("unknown union discriminator value: {key}"), })?; let variant_start = abs_offset .checked_add(disc_size) - .ok_or_else(|| TypedefError::Access { + .ok_or_else(|| AlkTypeError::Access { field_path: field_path.to_string(), reason: format!("variant start {abs_offset} + {disc_size} overflows usize"), })?; @@ -448,7 +448,7 @@ fn read_union_value<'a>( let end = variant_start .checked_add(variant_size) - .ok_or_else(|| TypedefError::Access { + .ok_or_else(|| AlkTypeError::Access { field_path: field_path.to_string(), reason: format!( "union end {variant_start} + {variant_size} overflows usize" @@ -468,19 +468,19 @@ fn read_union_value<'a>( .and_then(|obj| obj.get("properties")) .and_then(Value::as_object) .ok_or_else(|| { - TypedefError::Schema(format!( + AlkTypeError::Schema(format!( "field-name union {field_path} has no properties object" )) })?; let disc_schema = properties.get(&name).ok_or_else(|| { - TypedefError::Schema(format!( + AlkTypeError::Schema(format!( "union {field_path} has no discriminator field '{name}'" )) })?; let (disc_value, after_disc) = read_field_value(buffer, root_schema, disc_schema, field_path, offset, endian)?; let key = discriminator_string_value(&disc_value, field_path)?; - let variant_schema = mapping.get(&key).ok_or_else(|| TypedefError::Access { + let variant_schema = mapping.get(&key).ok_or_else(|| AlkTypeError::Access { field_path: field_path.to_string(), reason: format!("unknown union discriminator value: {key}"), })?; @@ -495,7 +495,7 @@ fn read_union_value<'a>( )?; let end = after_disc .checked_add(variant_size) - .ok_or_else(|| TypedefError::Access { + .ok_or_else(|| AlkTypeError::Access { field_path: field_path.to_string(), reason: format!("union end {after_disc} + {variant_size} overflows usize"), })?; @@ -516,23 +516,23 @@ fn read_byte_discriminator( buffer: &[u8], offset: usize, field_path: &str, - disc_type: TypeDefKind, + disc_type: AlkTypeKind, endian: Endian, -) -> Result<(u32, usize), TypedefError> { +) -> Result<(u32, usize), AlkTypeError> { match disc_type { - TypeDefKind::Uint8 => { + AlkTypeKind::Uint8 => { let v = data_access::read_u8(buffer, offset, field_path)?; Ok((v as u32, 1)) } - TypeDefKind::Uint16 => { + AlkTypeKind::Uint16 => { let v = data_access::read_u16(buffer, offset, field_path, endian)?; Ok((v as u32, 2)) } - TypeDefKind::Uint32 => { + AlkTypeKind::Uint32 => { let v = data_access::read_u32(buffer, offset, field_path, endian)?; Ok((v, 4)) } - other => Err(TypedefError::Schema(format!( + other => Err(AlkTypeError::Schema(format!( "unsupported byte discriminator type: {other}" ))), } @@ -544,20 +544,20 @@ fn read_byte_discriminator( fn discriminator_string_value( value: &FieldValue<'_>, field_path: &str, -) -> Result { +) -> Result { match value { FieldValue::String(s) => Ok(s.to_string()), FieldValue::U8(v) => Ok(v.to_string()), FieldValue::U16(v) => Ok(v.to_string()), FieldValue::U32(v) => Ok(v.to_string()), FieldValue::Enum(v) => Ok(v.to_string()), - other => Err(TypedefError::Schema(format!( + other => Err(AlkTypeError::Schema(format!( "union {field_path} has unsupported field discriminator kind: {other:?}" ))), } } -/// Read a `TypeDef:Array` field: read the count (fixed via `minItems`/ +/// Read a `AlkType:Array` field: read the count (fixed via `minItems`/ /// `maxItems` equality, or a 4-byte count prefix) and compute the /// element stride. Fixed-size elements produce a non-zero stride so the /// consumer can index directly; variable-length elements produce a @@ -569,13 +569,13 @@ fn read_array_value<'a>( field_path: &str, offset: usize, endian: Endian, -) -> Result<(FieldValue<'a>, usize), TypedefError> { +) -> Result<(FieldValue<'a>, usize), AlkTypeError> { let obj = schema.as_object().ok_or_else(|| { - TypedefError::Schema(format!("array {field_path} schema is not an object")) + AlkTypeError::Schema(format!("array {field_path} schema is not an object")) })?; let items_schema = obj .get("items") - .ok_or_else(|| TypedefError::Schema(format!("array {field_path} has no items schema")))?; + .ok_or_else(|| AlkTypeError::Schema(format!("array {field_path} has no items schema")))?; let min = obj .get("minItems") @@ -588,7 +588,7 @@ fn read_array_value<'a>( let fixed_count = matches!((min, max), (Some(a), Some(b)) if a == b); let (count, element_start) = if fixed_count { let count = min.ok_or_else(|| { - TypedefError::Schema(format!( + AlkTypeError::Schema(format!( "array {field_path} declared fixed count but minItems is absent" )) })?; @@ -598,9 +598,9 @@ fn read_array_value<'a>( (count, offset + U32_SIZE) }; - let element_kind = get_typedef_kind_loose_enum(items_schema).ok_or_else(|| { - TypedefError::Schema(format!( - "array {field_path} items schema has no TypeDef:* kind" + let element_kind = get_alktype_kind_loose_enum(items_schema).ok_or_else(|| { + AlkTypeError::Schema(format!( + "array {field_path} items schema has no AlkType:* kind" )) })?; @@ -623,7 +623,7 @@ fn read_array_value<'a>( } else { (count as usize) .checked_mul(element_stride) - .ok_or_else(|| TypedefError::Access { + .ok_or_else(|| AlkTypeError::Access { field_path: field_path.to_string(), reason: format!("array size {count} × stride {element_stride} overflows usize"), })? @@ -631,7 +631,7 @@ fn read_array_value<'a>( let end = element_start .checked_add(total) - .ok_or_else(|| TypedefError::Access { + .ok_or_else(|| AlkTypeError::Access { field_path: field_path.to_string(), reason: format!("array end {element_start} + {total} overflows usize"), })?; @@ -657,7 +657,7 @@ fn walk_variable_array_size( count: u32, endian: Endian, field_path: &str, -) -> Result { +) -> Result { let mut position = start; for i in 0..count { let element_path = format!("{field_path}[{i}]"); @@ -670,7 +670,7 @@ fn walk_variable_array_size( endian, )?; if new_position < position { - return Err(TypedefError::Access { + return Err(AlkTypeError::Access { field_path: element_path, reason: format!("array element walked backwards: {position} → {new_position}"), }); @@ -680,7 +680,7 @@ fn walk_variable_array_size( Ok(position - start) } -/// Read a `TypeDef:Record` field: `[count: u32]` followed by `count` +/// Read a `AlkType:Record` field: `[count: u32]` followed by `count` /// entries of `[key_len: u32][key_bytes][value]`. Returns the total size /// consumed. The reader does not decode the entries — the consumer /// recurses into the record's value schema. @@ -691,12 +691,12 @@ fn read_record_value<'a>( field_path: &str, offset: usize, endian: Endian, -) -> Result<(FieldValue<'a>, usize), TypedefError> { +) -> Result<(FieldValue<'a>, usize), AlkTypeError> { let count = data_access::read_u32(buffer, offset, field_path, endian)?; let value_schema = schema .as_object() .and_then(|obj| obj.get("values")) - .ok_or_else(|| TypedefError::Schema(format!("record {field_path} has no values schema")))?; + .ok_or_else(|| AlkTypeError::Schema(format!("record {field_path} has no values schema")))?; let mut position = offset + U32_SIZE; for i in 0..count { let entry_path = format!("{field_path}[{i}].key"); @@ -728,23 +728,23 @@ fn resolve_and_walk_variant( variant_start: usize, endian: Endian, field_path: &str, -) -> Result { +) -> Result { let resolved = resolve_variant_schema(root_schema, union_schema, variant_schema).ok_or_else(|| { - TypedefError::Schema(format!( + AlkTypeError::Schema(format!( "union {field_path} variant could not be resolved: {variant_schema}" )) })?; - let kind = get_typedef_kind_loose_enum(resolved).ok_or_else(|| { - TypedefError::Schema(format!( - "union {field_path} variant has no TypeDef:* kind: {resolved}" + let kind = get_alktype_kind_loose_enum(resolved).ok_or_else(|| { + AlkTypeError::Schema(format!( + "union {field_path} variant has no AlkType:* kind: {resolved}" )) })?; match kind { - TypeDefKind::Struct => { + AlkTypeKind::Struct => { walk_struct_size(root_schema, resolved, buffer, variant_start, endian) } - TypeDefKind::Union => { + AlkTypeKind::Union => { let (_, end) = read_union_value( buffer, root_schema, @@ -755,13 +755,13 @@ fn resolve_and_walk_variant( )?; Ok(end - variant_start) } - other => Err(TypedefError::Schema(format!( + other => Err(AlkTypeError::Schema(format!( "union {field_path} variant must be Struct or Union, got {other}" ))), } } -/// Resolve a variant schema. Inline schemas (objects with a `TypeDef:*` +/// Resolve a variant schema. Inline schemas (objects with a `AlkType:*` /// kind) are returned directly. `$ref` pointers of the form /// `#/$defs/` are resolved against `union_schema["$defs"]` first, /// then `root_schema["$defs"]`. Returns `None` if the ref cannot be @@ -789,7 +789,7 @@ fn resolve_variant_schema<'a>( } return None; } - if get_typedef_kind_loose_enum(variant).is_some() { + if get_alktype_kind_loose_enum(variant).is_some() { Some(variant) } else { None @@ -809,13 +809,13 @@ fn walk_struct_size( buffer: &[u8], offset: usize, endian: Endian, -) -> Result { +) -> Result { let properties = schema .as_object() .and_then(|obj| obj.get("properties")) .and_then(Value::as_object) .ok_or_else(|| { - TypedefError::Schema("struct schema has no properties object".to_string()) + AlkTypeError::Schema("struct schema has no properties object".to_string()) })?; let mut position = offset; for (name, field_schema) in properties.iter() { @@ -828,7 +828,7 @@ fn walk_struct_size( endian, )?; if new_position < position { - return Err(TypedefError::Access { + return Err(AlkTypeError::Access { field_path: name.clone(), reason: format!("struct field walked backwards: {position} → {new_position}"), }); @@ -865,12 +865,12 @@ mod tests { #[test] fn reads_fixed_size_fields_in_sequence() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "endian": "little", "properties": { - "a": { "TypeDef:Uint8": true }, - "b": { "TypeDef:Uint32": true }, - "c": { "TypeDef:Uint16": true } + "a": { "AlkType:Uint8": true }, + "b": { "AlkType:Uint32": true }, + "c": { "AlkType:Uint16": true } } }); let mut buf = vec![0u8; 16]; @@ -902,11 +902,11 @@ mod tests { #[test] fn reads_variable_length_string_with_length_prefix() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "id": { "TypeDef:Uint8": true }, - "name": { "TypeDef:String": true }, - "tail": { "TypeDef:Uint8": true } + "id": { "AlkType:Uint8": true }, + "name": { "AlkType:String": true }, + "tail": { "AlkType:Uint8": true } } }); let mut buf = vec![0u8; 32]; @@ -937,9 +937,9 @@ mod tests { #[test] fn reads_bytes_field() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "blob": { "TypeDef:Bytes": true } + "blob": { "AlkType:Bytes": true } } }); let mut buf = vec![0u8; 16]; @@ -957,10 +957,10 @@ mod tests { #[test] fn respects_big_endian() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "endian": "big", "properties": { - "id": { "TypeDef:Uint32": true } + "id": { "AlkType:Uint32": true } } }); let mut buf = vec![0u8; 8]; @@ -974,10 +974,10 @@ mod tests { #[test] fn reset_rewinds_cursor() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "a": { "TypeDef:Uint8": true }, - "b": { "TypeDef:Uint8": true } + "a": { "AlkType:Uint8": true }, + "b": { "AlkType:Uint8": true } } }); let buf = [10u8, 20u8]; @@ -996,11 +996,11 @@ mod tests { #[test] fn read_field_walks_preceding_fields() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "a": { "TypeDef:Uint8": true }, - "b": { "TypeDef:Uint32": true }, - "c": { "TypeDef:Uint8": true } + "a": { "AlkType:Uint8": true }, + "b": { "AlkType:Uint32": true }, + "c": { "AlkType:Uint8": true } } }); let mut buf = vec![0u8; 16]; @@ -1021,60 +1021,60 @@ mod tests { #[test] fn read_field_unknown_returns_schema_error() { let schema = json!({ - "TypeDef:Struct": true, - "properties": { "a": { "TypeDef:Uint8": true } } + "AlkType:Struct": true, + "properties": { "a": { "AlkType:Uint8": true } } }); let buf = [0u8; 4]; let mut reader = SequentialReader::new(&schema).unwrap(); let err = reader.read_field(&buf, "missing").unwrap_err(); - assert!(matches!(err, TypedefError::Schema(_)), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Schema(_)), "got {err:?}"); } #[test] fn buffer_too_short_returns_access_error() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "id": { "TypeDef:Uint32": true } + "id": { "AlkType:Uint32": true } } }); let buf = [0u8; 2]; let mut reader = SequentialReader::new(&schema).unwrap(); let err = reader.read_next(&buf).unwrap_err(); - assert!(matches!(err, TypedefError::Access { .. }), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Access { .. }), "got {err:?}"); } #[test] fn rejects_non_struct_top_level() { - let schema = json!({ "TypeDef:Uint32": true }); + let schema = json!({ "AlkType:Uint32": true }); let err = SequentialReader::new(&schema).unwrap_err(); - assert!(matches!(err, TypedefError::Schema(_)), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Schema(_)), "got {err:?}"); } #[test] - fn rejects_schema_without_typedef_kind() { + fn rejects_schema_without_alktype_kind() { let schema = json!({ "type": "object", "properties": {} }); let err = SequentialReader::new(&schema).unwrap_err(); - assert!(matches!(err, TypedefError::Schema(_)), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Schema(_)), "got {err:?}"); } #[test] fn reads_all_fixed_size_kinds() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "i8": { "TypeDef:Int8": true }, - "i16": { "TypeDef:Int16": true }, - "i32": { "TypeDef:Int32": true }, - "i64": { "TypeDef:Int64": true }, - "u8": { "TypeDef:Uint8": true }, - "u16": { "TypeDef:Uint16": true }, - "u32": { "TypeDef:Uint32": true }, - "u64": { "TypeDef:Uint64": true }, - "f32": { "TypeDef:Float32": true }, - "f64": { "TypeDef:Float64": true }, - "b": { "TypeDef:Boolean": true }, - "e": { "TypeDef:Enum": true } + "i8": { "AlkType:Int8": true }, + "i16": { "AlkType:Int16": true }, + "i32": { "AlkType:Int32": true }, + "i64": { "AlkType:Int64": true }, + "u8": { "AlkType:Uint8": true }, + "u16": { "AlkType:Uint16": true }, + "u32": { "AlkType:Uint32": true }, + "u64": { "AlkType:Uint64": true }, + "f32": { "AlkType:Float32": true }, + "f64": { "AlkType:Float64": true }, + "b": { "AlkType:Boolean": true }, + "e": { "AlkType:Enum": true } } }); let mut buf = vec![0u8; 80]; @@ -1122,16 +1122,16 @@ mod tests { #[test] fn nested_struct_reports_byte_range() { let nested = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { "inner": { - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "x": { "TypeDef:Uint8": true }, - "y": { "TypeDef:Uint16": true } + "x": { "AlkType:Uint8": true }, + "y": { "AlkType:Uint16": true } } }, - "tail": { "TypeDef:Uint8": true } + "tail": { "AlkType:Uint8": true } } }); let mut buf = vec![0u8; 16]; @@ -1166,17 +1166,17 @@ mod tests { #[test] fn byte_discriminator_union_reads_value() { let variant = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "x": { "TypeDef:Uint8": true } + "x": { "AlkType:Uint8": true } } }); let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { "packet": { - "TypeDef:Union": true, - "discriminator": { "kind": "byte", "offset": 0, "type": "TypeDef:Uint8" }, + "AlkType:Union": true, + "discriminator": { "kind": "byte", "offset": 0, "type": "AlkType:Uint8" }, "mapping": { "5": variant.clone() }, "$defs": { "Read": variant.clone() } } @@ -1205,19 +1205,19 @@ mod tests { #[test] fn field_discriminator_union_reads_value() { let variant = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "payload": { "TypeDef:Uint8": true } + "payload": { "AlkType:Uint8": true } } }); let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { "event": { - "TypeDef:Union": true, + "AlkType:Union": true, "discriminator": { "kind": "field", "name": "type" }, "properties": { - "type": { "TypeDef:String": true } + "type": { "AlkType:String": true } }, "mapping": { "read": variant.clone() } } @@ -1247,13 +1247,13 @@ mod tests { #[test] fn fixed_count_array_reads_count_inline() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { "vals": { - "TypeDef:Array": true, + "AlkType:Array": true, "minItems": 3, "maxItems": 3, - "items": { "TypeDef:Uint8": true } + "items": { "AlkType:Uint8": true } } } }); @@ -1280,11 +1280,11 @@ mod tests { #[test] fn variable_count_array_reads_count_prefix() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { "vals": { - "TypeDef:Array": true, - "items": { "TypeDef:Uint16": true } + "AlkType:Array": true, + "items": { "AlkType:Uint16": true } } } }); @@ -1314,11 +1314,11 @@ mod tests { #[test] fn variable_length_element_array_walks_sequentially() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { "items": { - "TypeDef:Array": true, - "items": { "TypeDef:String": true } + "AlkType:Array": true, + "items": { "AlkType:String": true } } } }); @@ -1349,9 +1349,9 @@ mod tests { #[test] fn timestamp_field_reads_as_length_prefixed_string() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "ts": { "TypeDef:Timestamp": true } + "ts": { "AlkType:Timestamp": true } } }); let mut buf = vec![0u8; 64]; @@ -1368,11 +1368,11 @@ mod tests { #[test] fn record_field_walks_entries() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { "counts": { - "TypeDef:Record": true, - "values": { "TypeDef:Uint32": true } + "AlkType:Record": true, + "values": { "AlkType:Uint32": true } } } }); @@ -1395,15 +1395,15 @@ mod tests { #[test] fn union_unknown_discriminator_returns_access_error() { let variant = json!({ - "TypeDef:Struct": true, - "properties": { "x": { "TypeDef:Uint8": true } } + "AlkType:Struct": true, + "properties": { "x": { "AlkType:Uint8": true } } }); let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { "packet": { - "TypeDef:Union": true, - "discriminator": { "kind": "byte", "offset": 0, "type": "TypeDef:Uint8" }, + "AlkType:Union": true, + "discriminator": { "kind": "byte", "offset": 0, "type": "AlkType:Uint8" }, "mapping": { "5": variant }, "$defs": { "Read": variant } } @@ -1412,22 +1412,22 @@ mod tests { let buf = [99u8, 0]; let mut reader = SequentialReader::new(&schema).unwrap(); let err = reader.read_next(&buf).unwrap_err(); - assert!(matches!(err, TypedefError::Access { .. }), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Access { .. }), "got {err:?}"); } #[test] fn union_via_ref_resolves_variant() { let read_variant = json!({ - "TypeDef:Struct": true, - "properties": { "len": { "TypeDef:Uint32": true } } + "AlkType:Struct": true, + "properties": { "len": { "AlkType:Uint32": true } } }); let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "$defs": { "Read": read_variant }, "properties": { "packet": { - "TypeDef:Union": true, - "discriminator": { "kind": "byte", "offset": 0, "type": "TypeDef:Uint8" }, + "AlkType:Union": true, + "discriminator": { "kind": "byte", "offset": 0, "type": "AlkType:Uint8" }, "mapping": { "5": { "$ref": "#/$defs/Read" } } } } @@ -1455,15 +1455,15 @@ mod tests { #[test] fn union_via_ref_with_local_defs_resolves_variant() { let read_variant = json!({ - "TypeDef:Struct": true, - "properties": { "len": { "TypeDef:Uint32": true } } + "AlkType:Struct": true, + "properties": { "len": { "AlkType:Uint32": true } } }); let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { "packet": { - "TypeDef:Union": true, - "discriminator": { "kind": "byte", "offset": 0, "type": "TypeDef:Uint8" }, + "AlkType:Union": true, + "discriminator": { "kind": "byte", "offset": 0, "type": "AlkType:Uint8" }, "mapping": { "5": { "$ref": "#/$defs/Read" } }, "$defs": { "Read": read_variant } } @@ -1492,7 +1492,7 @@ mod tests { #[test] fn empty_struct_returns_none_immediately() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": {} }); let buf = []; @@ -1504,16 +1504,16 @@ mod tests { #[test] fn nested_struct_with_variable_field_computes_end() { let nested = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { "header": { - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "id": { "TypeDef:Uint8": true }, - "name": { "TypeDef:String": true } + "id": { "AlkType:Uint8": true }, + "name": { "AlkType:String": true } } }, - "tail": { "TypeDef:Uint8": true } + "tail": { "AlkType:Uint8": true } } }); let mut buf = vec![0u8; 64]; diff --git a/src/tunion.rs b/src/tunion.rs index dd17505..4d17e5d 100644 --- a/src/tunion.rs +++ b/src/tunion.rs @@ -10,8 +10,8 @@ //! endianness handling are uniform with the rest of the engine. use crate::data_access::{read_enum, read_string, read_u16, read_u32, read_u8}; -use crate::error::TypedefError; -use crate::schema::{get_typedef_kind, parse_discriminator, DiscriminatorKind, Endian, TypeDefKind, DISCRIMINATOR_PATH, U32_SIZE}; +use crate::error::AlkTypeError; +use crate::schema::{get_alktype_kind, parse_discriminator, DiscriminatorKind, Endian, AlkTypeKind, DISCRIMINATOR_PATH, U32_SIZE}; use serde_json::Value; const STRING_PREFIX_SIZE: usize = 4; @@ -39,36 +39,36 @@ pub struct UnionDispatch { /// /// # Errors /// -/// - [`TypedefError::Schema`] if the discriminator annotation is missing +/// - [`AlkTypeError::Schema`] if the discriminator annotation is missing /// or malformed, or if the discriminator `type` is not one of -/// `TypeDef:Uint8` / `TypeDef:Uint16` / `TypeDef:Uint32`. -/// - [`TypedefError::Access`] if the buffer is too short to contain the +/// `AlkType:Uint8` / `AlkType:Uint16` / `AlkType:Uint32`. +/// - [`AlkTypeError::Access`] if the buffer is too short to contain the /// discriminator, or if the read value is not present in the union's /// `mapping`. pub fn read_byte_discriminator( buffer: &[u8], union_schema: &Value, endian: Endian, -) -> Result { +) -> Result { let disc = parse_discriminator(union_schema)?; let (offset, disc_type) = match disc { DiscriminatorKind::Byte { offset, disc_type } => (offset, disc_type), DiscriminatorKind::Field { .. } => { - return Err(TypedefError::Schema( + return Err(AlkTypeError::Schema( "read_byte_discriminator requires a byte-offset discriminator".to_string(), )); } }; let (disc_value, discriminator_size) = match disc_type { - TypeDefKind::Uint8 => (u32::from(read_u8(buffer, offset, DISCRIMINATOR_PATH)?), 1), - TypeDefKind::Uint16 => ( + AlkTypeKind::Uint8 => (u32::from(read_u8(buffer, offset, DISCRIMINATOR_PATH)?), 1), + AlkTypeKind::Uint16 => ( u32::from(read_u16(buffer, offset, DISCRIMINATOR_PATH, endian)?), 2, ), - TypeDefKind::Uint32 => (read_u32(buffer, offset, DISCRIMINATOR_PATH, endian)?, 4), + AlkTypeKind::Uint32 => (read_u32(buffer, offset, DISCRIMINATOR_PATH, endian)?, 4), other => { - return Err(TypedefError::Schema(format!( + return Err(AlkTypeError::Schema(format!( "unsupported byte discriminator type: {other}" ))); } @@ -80,7 +80,7 @@ pub fn read_byte_discriminator( let variant_offset = offset .checked_add(discriminator_size) - .ok_or_else(|| TypedefError::Access { + .ok_or_else(|| AlkTypeError::Access { field_path: DISCRIMINATOR_PATH.to_string(), reason: format!( "offset {offset} + discriminator_size {discriminator_size} overflows usize" @@ -105,12 +105,12 @@ pub fn read_byte_discriminator( /// /// # Errors /// -/// - [`TypedefError::Schema`] if the discriminator annotation is missing +/// - [`AlkTypeError::Schema`] if the discriminator annotation is missing /// or malformed, the discriminator field is not declared in -/// `properties`, the field has no `TypeDef:*` kind, or the field's -/// kind is not one of `TypeDef:String` / `TypeDef:Uint8` / -/// `TypeDef:Enum`. -/// - [`TypedefError::Access`] if the buffer is too short to contain the +/// `properties`, the field has no `AlkType:*` kind, or the field's +/// kind is not one of `AlkType:String` / `AlkType:Uint8` / +/// `AlkType:Enum`. +/// - [`AlkTypeError::Access`] if the buffer is too short to contain the /// discriminator field, or if the read value is not present in the /// union's `mapping`. pub fn read_field_discriminator( @@ -118,12 +118,12 @@ pub fn read_field_discriminator( union_schema: &Value, disc_field_offset: usize, endian: Endian, -) -> Result { +) -> Result { let disc = parse_discriminator(union_schema)?; let name = match disc { DiscriminatorKind::Field { name } => name, DiscriminatorKind::Byte { .. } => { - return Err(TypedefError::Schema( + return Err(AlkTypeError::Schema( "read_field_discriminator requires a field-name discriminator".to_string(), )); } @@ -134,26 +134,26 @@ pub fn read_field_discriminator( .and_then(Value::as_object) .and_then(|props| props.get(&name)) .ok_or_else(|| { - TypedefError::Schema(format!( + AlkTypeError::Schema(format!( "discriminator field '{name}' not found in union properties" )) })?; - let kind = get_typedef_kind(field_schema) - .and_then(|s| s.parse::().ok()) + let kind = get_alktype_kind(field_schema) + .and_then(|s| s.parse::().ok()) .ok_or_else(|| { - TypedefError::Schema(format!( - "discriminator field '{name}' has no TypeDef:* kind" + AlkTypeError::Schema(format!( + "discriminator field '{name}' has no AlkType:* kind" )) })?; let (key, discriminator_field_size) = match kind { - TypeDefKind::String => { + AlkTypeKind::String => { let s = read_string(buffer, disc_field_offset, &name, endian)?; let size = STRING_PREFIX_SIZE .checked_add(s.len()) - .ok_or_else(|| TypedefError::Access { + .ok_or_else(|| AlkTypeError::Access { field_path: name.clone(), reason: format!( "string prefix {STRING_PREFIX_SIZE} + data length {} overflows usize", @@ -162,16 +162,16 @@ pub fn read_field_discriminator( })?; (s.to_string(), size) } - TypeDefKind::Uint8 => { + AlkTypeKind::Uint8 => { let v = read_u8(buffer, disc_field_offset, &name)?; (v.to_string(), 1) } - TypeDefKind::Enum => { + AlkTypeKind::Enum => { let v = read_enum(buffer, disc_field_offset, &name, endian)?; (v.to_string(), U32_SIZE) } other => { - return Err(TypedefError::Schema(format!( + return Err(AlkTypeError::Schema(format!( "unsupported discriminator field type: {other}" ))); } @@ -181,7 +181,7 @@ pub fn read_field_discriminator( let variant_offset = disc_field_offset .checked_add(discriminator_field_size) - .ok_or_else(|| TypedefError::Access { + .ok_or_else(|| AlkTypeError::Access { field_path: name.clone(), reason: format!( "disc_field_offset {disc_field_offset} + discriminator_field_size {discriminator_field_size} overflows usize" @@ -201,24 +201,24 @@ pub fn read_field_discriminator( /// `$ref` pointers of the form `"#/$defs/"` are resolved against /// the `union_schema`'s own `$defs` block (when the union schema is the /// schema root). For nested unions whose `$defs` live on an ancestor, -/// the caller (typically `TypedefEngine::compile`) is expected to +/// the caller (typically `AlkTypeEngine::compile`) is expected to /// resolve refs before reaching this function, or to inline the /// variant schemas into the mapping at load time. /// /// # Errors /// -/// - [`TypedefError::Schema`] if the union has no `mapping` object, the +/// - [`AlkTypeError::Schema`] if the union has no `mapping` object, the /// `key` is not present, a `$ref` is malformed, or a `$ref` cannot be /// resolved against the union schema's own `$defs`. -pub fn resolve_variant<'a>(union_schema: &'a Value, key: &str) -> Result<&'a Value, TypedefError> { +pub fn resolve_variant<'a>(union_schema: &'a Value, key: &str) -> Result<&'a Value, AlkTypeError> { let mapping = union_schema .get("mapping") .and_then(Value::as_object) - .ok_or_else(|| TypedefError::Schema("union is missing 'mapping' object".to_string()))?; + .ok_or_else(|| AlkTypeError::Schema("union is missing 'mapping' object".to_string()))?; let variant = mapping .get(key) - .ok_or_else(|| TypedefError::Schema(format!("unknown mapping key: {key}")))?; + .ok_or_else(|| AlkTypeError::Schema(format!("unknown mapping key: {key}")))?; let ref_str = match variant.get("$ref").and_then(Value::as_str) { Some(r) => r, @@ -227,10 +227,10 @@ pub fn resolve_variant<'a>(union_schema: &'a Value, key: &str) -> Result<&'a Val let pointer = ref_str .strip_prefix('#') - .ok_or_else(|| TypedefError::Schema(format!("unsupported $ref form: {ref_str}")))?; + .ok_or_else(|| AlkTypeError::Schema(format!("unsupported $ref form: {ref_str}")))?; let resolved = resolve_json_pointer(union_schema, pointer).ok_or_else(|| { - TypedefError::Schema(format!( + AlkTypeError::Schema(format!( "cannot resolve $ref {ref_str} against union schema; ensure refs are inlined or the union schema contains $defs" )) })?; @@ -239,27 +239,27 @@ pub fn resolve_variant<'a>(union_schema: &'a Value, key: &str) -> Result<&'a Val /// Get the discriminator size in bytes for a byte-offset discriminator. /// -/// Returns 1 for `TypeDef:Uint8`, 2 for `TypeDef:Uint16`, and 4 for -/// `TypeDef:Uint32`. Field-name discriminators have no fixed size and -/// produce a [`TypedefError::Schema`]. +/// Returns 1 for `AlkType:Uint8`, 2 for `AlkType:Uint16`, and 4 for +/// `AlkType:Uint32`. Field-name discriminators have no fixed size and +/// produce a [`AlkTypeError::Schema`]. /// /// # Errors /// -/// - [`TypedefError::Schema`] if the discriminator annotation is +/// - [`AlkTypeError::Schema`] if the discriminator annotation is /// missing/malformed, the discriminator `type` is unsupported, or the /// discriminator is a field-name discriminator. -pub fn discriminator_size(union_schema: &Value) -> Result { +pub fn discriminator_size(union_schema: &Value) -> Result { let disc = parse_discriminator(union_schema)?; match disc { DiscriminatorKind::Byte { disc_type, .. } => match disc_type { - TypeDefKind::Uint8 => Ok(1), - TypeDefKind::Uint16 => Ok(2), - TypeDefKind::Uint32 => Ok(4), - other => Err(TypedefError::Schema(format!( + AlkTypeKind::Uint8 => Ok(1), + AlkTypeKind::Uint16 => Ok(2), + AlkTypeKind::Uint32 => Ok(4), + other => Err(AlkTypeError::Schema(format!( "unsupported byte discriminator type: {other}" ))), }, - DiscriminatorKind::Field { .. } => Err(TypedefError::Schema( + DiscriminatorKind::Field { .. } => Err(AlkTypeError::Schema( "field-name discriminator has no fixed size".to_string(), )), } @@ -270,7 +270,7 @@ fn verify_mapping_key( key: &str, field_path: &str, raw_value: &str, -) -> Result<(), TypedefError> { +) -> Result<(), AlkTypeError> { let in_mapping = union_schema .get("mapping") .and_then(Value::as_object) @@ -279,7 +279,7 @@ fn verify_mapping_key( if in_mapping { Ok(()) } else { - Err(TypedefError::Access { + Err(AlkTypeError::Access { field_path: field_path.to_string(), reason: format!("unknown discriminator value: {raw_value}"), }) @@ -325,43 +325,43 @@ mod tests { fn byte_union_schema(offset: usize, disc_type: &str) -> Value { json!({ - "TypeDef:Union": true, + "AlkType:Union": true, "discriminator": {"kind": "byte", "offset": offset, "type": disc_type}, "mapping": { - "5": {"TypeDef:Struct": true, "properties": {"id": {"TypeDef:Uint32": true}}}, - "6": {"TypeDef:Struct": true, "properties": {"len": {"TypeDef:Uint16": true}}} + "5": {"AlkType:Struct": true, "properties": {"id": {"AlkType:Uint32": true}}}, + "6": {"AlkType:Struct": true, "properties": {"len": {"AlkType:Uint16": true}}} } }) } fn field_union_schema(field_name: &str, field_kind: &str) -> Value { let field_schema = match field_kind { - "TypeDef:Enum" => json!({ - "TypeDef:Enum": true, + "AlkType:Enum" => json!({ + "AlkType:Enum": true, "enum": ["read", "write"] }), _ => json!({field_kind: true}), }; let (key_a, key_b) = match field_kind { - "TypeDef:String" => ("read", "write"), + "AlkType:String" => ("read", "write"), _ => ("0", "1"), }; json!({ - "TypeDef:Union": true, + "AlkType:Union": true, "discriminator": {"kind": "field", "name": field_name}, "properties": { field_name: field_schema }, "mapping": { - key_a: {"TypeDef:Struct": true, "properties": {"n": {"TypeDef:Uint32": true}}}, - key_b: {"TypeDef:Struct": true, "properties": {"m": {"TypeDef:Uint16": true}}} + key_a: {"AlkType:Struct": true, "properties": {"n": {"AlkType:Uint32": true}}}, + key_b: {"AlkType:Struct": true, "properties": {"m": {"AlkType:Uint16": true}}} } }) } #[test] fn read_byte_discriminator_uint8_default_offset() { - let schema = byte_union_schema(0, "TypeDef:Uint8"); + let schema = byte_union_schema(0, "AlkType:Uint8"); let buf = [5u8, 0xAA, 0xBB, 0xCC]; let d = read_byte_discriminator(&buf, &schema, LE).expect("read"); assert_eq!(d.key, "5"); @@ -371,7 +371,7 @@ mod tests { #[test] fn read_byte_discriminator_uint8_big_endian() { - let schema = byte_union_schema(0, "TypeDef:Uint8"); + let schema = byte_union_schema(0, "AlkType:Uint8"); let buf = [6u8]; let d = read_byte_discriminator(&buf, &schema, BE).expect("read"); assert_eq!(d.key, "6"); @@ -380,7 +380,7 @@ mod tests { #[test] fn read_byte_discriminator_uint16_little_endian() { - let schema = byte_union_schema(2, "TypeDef:Uint16"); + let schema = byte_union_schema(2, "AlkType:Uint16"); let mut buf = vec![0u8; 4]; buf[2..4].copy_from_slice(&5u16.to_le_bytes()); let d = read_byte_discriminator(&buf, &schema, LE).expect("read"); @@ -391,7 +391,7 @@ mod tests { #[test] fn read_byte_discriminator_uint16_big_endian() { - let schema = byte_union_schema(0, "TypeDef:Uint16"); + let schema = byte_union_schema(0, "AlkType:Uint16"); let buf = [0x00, 0x06, 0xAA, 0xBB]; let d = read_byte_discriminator(&buf, &schema, BE).expect("read"); assert_eq!(d.key, "6"); @@ -400,7 +400,7 @@ mod tests { #[test] fn read_byte_discriminator_uint32_little_endian() { - let schema = byte_union_schema(0, "TypeDef:Uint32"); + let schema = byte_union_schema(0, "AlkType:Uint32"); let mut buf = vec![0u8; 8]; buf[0..4].copy_from_slice(&5u32.to_le_bytes()); let d = read_byte_discriminator(&buf, &schema, LE).expect("read"); @@ -411,7 +411,7 @@ mod tests { #[test] fn read_byte_discriminator_uint32_big_endian() { - let schema = byte_union_schema(0, "TypeDef:Uint32"); + let schema = byte_union_schema(0, "AlkType:Uint32"); let mut buf = vec![0u8; 8]; buf[0..4].copy_from_slice(&6u32.to_be_bytes()); let d = read_byte_discriminator(&buf, &schema, BE).expect("read"); @@ -421,11 +421,11 @@ mod tests { #[test] fn read_byte_discriminator_unknown_value_is_access_error() { - let schema = byte_union_schema(0, "TypeDef:Uint8"); + let schema = byte_union_schema(0, "AlkType:Uint8"); let buf = [99u8]; let err = read_byte_discriminator(&buf, &schema, LE).unwrap_err(); match err { - TypedefError::Access { field_path, reason } => { + AlkTypeError::Access { field_path, reason } => { assert_eq!(field_path, DISCRIMINATOR_PATH); assert!(reason.contains("99"), "reason: {reason}"); } @@ -435,23 +435,23 @@ mod tests { #[test] fn read_byte_discriminator_buffer_too_short_is_access_error() { - let schema = byte_union_schema(4, "TypeDef:Uint32"); + let schema = byte_union_schema(4, "AlkType:Uint32"); let buf = [0u8; 2]; let err = read_byte_discriminator(&buf, &schema, LE).unwrap_err(); - assert!(matches!(err, TypedefError::Access { .. })); + assert!(matches!(err, AlkTypeError::Access { .. })); } #[test] fn read_byte_discriminator_field_kind_is_schema_error() { - let schema = field_union_schema("type", "TypeDef:String"); + let schema = field_union_schema("type", "AlkType:String"); let buf = [0u8; 16]; let err = read_byte_discriminator(&buf, &schema, LE).unwrap_err(); - assert!(matches!(err, TypedefError::Schema(_))); + assert!(matches!(err, AlkTypeError::Schema(_))); } #[test] fn read_field_discriminator_string() { - let schema = field_union_schema("type", "TypeDef:String"); + let schema = field_union_schema("type", "AlkType:String"); let mut buf = vec![0u8; 32]; let value = "read"; let len_bytes = (value.len() as u32).to_le_bytes(); @@ -465,7 +465,7 @@ mod tests { #[test] fn read_field_discriminator_uint8() { - let schema = field_union_schema("type", "TypeDef:Uint8"); + let schema = field_union_schema("type", "AlkType:Uint8"); let mut buf = vec![0u8; 8]; buf[0] = 0; let d = read_field_discriminator(&buf, &schema, 0, LE).expect("read"); @@ -476,7 +476,7 @@ mod tests { #[test] fn read_field_discriminator_enum() { - let schema = field_union_schema("type", "TypeDef:Enum"); + let schema = field_union_schema("type", "AlkType:Enum"); let mut buf = vec![0u8; 8]; buf[0..4].copy_from_slice(&0u32.to_le_bytes()); let d = read_field_discriminator(&buf, &schema, 0, LE).expect("read"); @@ -487,7 +487,7 @@ mod tests { #[test] fn read_field_discriminator_string_big_endian() { - let schema = field_union_schema("type", "TypeDef:String"); + let schema = field_union_schema("type", "AlkType:String"); let mut buf = vec![0u8; 32]; let value = "write"; let len_bytes = (value.len() as u32).to_be_bytes(); @@ -500,12 +500,12 @@ mod tests { #[test] fn read_field_discriminator_unknown_value_is_access_error() { - let schema = field_union_schema("type", "TypeDef:Uint8"); + let schema = field_union_schema("type", "AlkType:Uint8"); let mut buf = vec![0u8; 8]; buf[0] = 99; let err = read_field_discriminator(&buf, &schema, 0, LE).unwrap_err(); match err { - TypedefError::Access { field_path, reason } => { + AlkTypeError::Access { field_path, reason } => { assert_eq!(field_path, "type"); assert!(reason.contains("99"), "reason: {reason}"); } @@ -516,51 +516,51 @@ mod tests { #[test] fn read_field_discriminator_field_not_found_is_schema_error() { let schema = json!({ - "TypeDef:Union": true, + "AlkType:Union": true, "discriminator": {"kind": "field", "name": "missing"}, - "properties": {"other": {"TypeDef:Uint8": true}}, - "mapping": {"5": {"TypeDef:Struct": true}} + "properties": {"other": {"AlkType:Uint8": true}}, + "mapping": {"5": {"AlkType:Struct": true}} }); let buf = [0u8; 4]; let err = read_field_discriminator(&buf, &schema, 0, LE).unwrap_err(); - assert!(matches!(err, TypedefError::Schema(_))); + assert!(matches!(err, AlkTypeError::Schema(_))); } #[test] - fn read_field_discriminator_no_typedef_kind_is_schema_error() { + fn read_field_discriminator_no_alktype_kind_is_schema_error() { let schema = json!({ - "TypeDef:Union": true, + "AlkType:Union": true, "discriminator": {"kind": "field", "name": "type"}, "properties": {"type": {"type": "string"}}, - "mapping": {"read": {"TypeDef:Struct": true}} + "mapping": {"read": {"AlkType:Struct": true}} }); let buf = [0u8; 4]; let err = read_field_discriminator(&buf, &schema, 0, LE).unwrap_err(); - assert!(matches!(err, TypedefError::Schema(_))); + assert!(matches!(err, AlkTypeError::Schema(_))); } #[test] fn read_field_discriminator_unsupported_kind_is_schema_error() { - let schema = field_union_schema("type", "TypeDef:Float32"); + let schema = field_union_schema("type", "AlkType:Float32"); let buf = [0u8; 8]; let err = read_field_discriminator(&buf, &schema, 0, LE).unwrap_err(); - assert!(matches!(err, TypedefError::Schema(_))); + assert!(matches!(err, AlkTypeError::Schema(_))); } #[test] fn read_field_discriminator_byte_kind_is_schema_error() { - let schema = byte_union_schema(0, "TypeDef:Uint8"); + let schema = byte_union_schema(0, "AlkType:Uint8"); let buf = [5u8]; let err = read_field_discriminator(&buf, &schema, 0, LE).unwrap_err(); - assert!(matches!(err, TypedefError::Schema(_))); + assert!(matches!(err, AlkTypeError::Schema(_))); } #[test] fn resolve_variant_inline_schema() { - let schema = byte_union_schema(0, "TypeDef:Uint8"); + let schema = byte_union_schema(0, "AlkType:Uint8"); let variant = resolve_variant(&schema, "5").expect("resolve"); assert_eq!( - variant.get("TypeDef:Struct").and_then(Value::as_bool), + variant.get("AlkType:Struct").and_then(Value::as_bool), Some(true) ); } @@ -568,78 +568,78 @@ mod tests { #[test] fn resolve_variant_ref_against_own_defs() { let schema = json!({ - "TypeDef:Union": true, + "AlkType:Union": true, "discriminator": {"kind": "byte"}, "mapping": { "5": {"$ref": "#/$defs/Read"} }, "$defs": { - "Read": {"TypeDef:Struct": true, "properties": {"id": {"TypeDef:Uint32": true}}} + "Read": {"AlkType:Struct": true, "properties": {"id": {"AlkType:Uint32": true}}} } }); let variant = resolve_variant(&schema, "5").expect("resolve"); assert_eq!( - variant.get("TypeDef:Struct").and_then(Value::as_bool), + variant.get("AlkType:Struct").and_then(Value::as_bool), Some(true) ); } #[test] fn resolve_variant_unknown_key_is_schema_error() { - let schema = byte_union_schema(0, "TypeDef:Uint8"); + let schema = byte_union_schema(0, "AlkType:Uint8"); let err = resolve_variant(&schema, "999").unwrap_err(); - assert!(matches!(err, TypedefError::Schema(_))); + assert!(matches!(err, AlkTypeError::Schema(_))); } #[test] fn resolve_variant_missing_mapping_is_schema_error() { - let schema = json!({"TypeDef:Union": true, "discriminator": {"kind": "byte"}}); + let schema = json!({"AlkType:Union": true, "discriminator": {"kind": "byte"}}); let err = resolve_variant(&schema, "5").unwrap_err(); - assert!(matches!(err, TypedefError::Schema(_))); + assert!(matches!(err, AlkTypeError::Schema(_))); } #[test] fn resolve_variant_unresolvable_ref_is_schema_error() { let schema = json!({ - "TypeDef:Union": true, + "AlkType:Union": true, "discriminator": {"kind": "byte"}, "mapping": { "5": {"$ref": "#/$defs/Read"} } }); let err = resolve_variant(&schema, "5").unwrap_err(); - assert!(matches!(err, TypedefError::Schema(_))); + assert!(matches!(err, AlkTypeError::Schema(_))); } #[test] fn discriminator_size_uint8() { - let schema = byte_union_schema(0, "TypeDef:Uint8"); + let schema = byte_union_schema(0, "AlkType:Uint8"); assert_eq!(discriminator_size(&schema).unwrap(), 1); } #[test] fn discriminator_size_uint16() { - let schema = byte_union_schema(0, "TypeDef:Uint16"); + let schema = byte_union_schema(0, "AlkType:Uint16"); assert_eq!(discriminator_size(&schema).unwrap(), 2); } #[test] fn discriminator_size_uint32() { - let schema = byte_union_schema(0, "TypeDef:Uint32"); + let schema = byte_union_schema(0, "AlkType:Uint32"); assert_eq!(discriminator_size(&schema).unwrap(), 4); } #[test] fn discriminator_size_field_kind_is_schema_error() { - let schema = field_union_schema("type", "TypeDef:String"); + let schema = field_union_schema("type", "AlkType:String"); let err = discriminator_size(&schema).unwrap_err(); - assert!(matches!(err, TypedefError::Schema(_))); + assert!(matches!(err, AlkTypeError::Schema(_))); } #[test] fn discriminator_size_missing_discriminator_is_schema_error() { - let schema = json!({"TypeDef:Union": true}); + let schema = json!({"AlkType:Union": true}); let err = discriminator_size(&schema).unwrap_err(); - assert!(matches!(err, TypedefError::Schema(_))); + assert!(matches!(err, AlkTypeError::Schema(_))); } } diff --git a/src/validation.rs b/src/validation.rs index 96feca9..1996ec7 100644 --- a/src/validation.rs +++ b/src/validation.rs @@ -1,4 +1,4 @@ -//! Custom keyword validators for all 17 `TypeDef:*` kinds, registered +//! Custom keyword validators for all 17 `AlkType:*` kinds, registered //! via `jsonschema::options().with_keyword(...)`. //! //! Per ADR-098: the `jsonschema` crate handles all structural validation; @@ -10,11 +10,11 @@ //! factories read parent context (e.g. `maxLength`) to pass into the //! validator struct. -use crate::error::TypedefError; +use crate::error::AlkTypeError; use jsonschema::{Keyword, ValidationError}; use serde_json::{Map, Value}; -/// Build a jsonschema validator with all 17 `TypeDef:*` custom keywords +/// Build a jsonschema validator with all 17 `AlkType:*` custom keywords /// registered. /// /// The returned validator can validate JSON representations of data @@ -25,43 +25,43 @@ use serde_json::{Map, Value}; /// /// # Errors /// -/// Returns [`TypedefError::Schema`] if the schema is malformed or the +/// Returns [`AlkTypeError::Schema`] if the schema is malformed or the /// underlying jsonschema validator cannot be built. -pub fn build_validator(schema: &Value) -> Result { +pub fn build_validator(schema: &Value) -> Result { jsonschema::options() - .with_keyword("TypeDef:Float32", float32_factory) - .with_keyword("TypeDef:Float64", float64_factory) - .with_keyword("TypeDef:Int8", int8_factory) - .with_keyword("TypeDef:Int16", int16_factory) - .with_keyword("TypeDef:Int32", int32_factory) - .with_keyword("TypeDef:Int64", int64_factory) - .with_keyword("TypeDef:Uint8", uint8_factory) - .with_keyword("TypeDef:Uint16", uint16_factory) - .with_keyword("TypeDef:Uint32", uint32_factory) - .with_keyword("TypeDef:Uint64", uint64_factory) - .with_keyword("TypeDef:Boolean", boolean_factory) - .with_keyword("TypeDef:String", string_factory) - .with_keyword("TypeDef:Bytes", bytes_factory) - .with_keyword("TypeDef:Enum", enum_factory) - .with_keyword("TypeDef:Struct", struct_factory) - .with_keyword("TypeDef:Union", union_factory) - .with_keyword("TypeDef:Array", array_factory) - .with_keyword("TypeDef:Record", record_factory) - .with_keyword("TypeDef:Timestamp", timestamp_factory) + .with_keyword("AlkType:Float32", float32_factory) + .with_keyword("AlkType:Float64", float64_factory) + .with_keyword("AlkType:Int8", int8_factory) + .with_keyword("AlkType:Int16", int16_factory) + .with_keyword("AlkType:Int32", int32_factory) + .with_keyword("AlkType:Int64", int64_factory) + .with_keyword("AlkType:Uint8", uint8_factory) + .with_keyword("AlkType:Uint16", uint16_factory) + .with_keyword("AlkType:Uint32", uint32_factory) + .with_keyword("AlkType:Uint64", uint64_factory) + .with_keyword("AlkType:Boolean", boolean_factory) + .with_keyword("AlkType:String", string_factory) + .with_keyword("AlkType:Bytes", bytes_factory) + .with_keyword("AlkType:Enum", enum_factory) + .with_keyword("AlkType:Struct", struct_factory) + .with_keyword("AlkType:Union", union_factory) + .with_keyword("AlkType:Array", array_factory) + .with_keyword("AlkType:Record", record_factory) + .with_keyword("AlkType:Timestamp", timestamp_factory) .build(schema) - .map_err(|e| TypedefError::Schema(format!("validator build failed: {e}"))) + .map_err(|e| AlkTypeError::Schema(format!("validator build failed: {e}"))) } // --------------------------------------------------------------------------- // Numeric validators (generated via macros) // --------------------------------------------------------------------------- -define_int_validator!(Int8Validator, int8_factory, "TypeDef:Int8", -128, 127); -define_int_validator!(Int16Validator, int16_factory, "TypeDef:Int16", -32768, 32767); -define_int_validator!(Int32Validator, int32_factory, "TypeDef:Int32", -2147483648, 2147483647); -define_uint_validator!(Uint8Validator, uint8_factory, "TypeDef:Uint8", 255); -define_uint_validator!(Uint16Validator, uint16_factory, "TypeDef:Uint16", 65535); -define_uint_validator!(Uint32Validator, uint32_factory, "TypeDef:Uint32", 4294967295); +define_int_validator!(Int8Validator, int8_factory, "AlkType:Int8", -128, 127); +define_int_validator!(Int16Validator, int16_factory, "AlkType:Int16", -32768, 32767); +define_int_validator!(Int32Validator, int32_factory, "AlkType:Int32", -2147483648, 2147483647); +define_uint_validator!(Uint8Validator, uint8_factory, "AlkType:Uint8", 255); +define_uint_validator!(Uint16Validator, uint16_factory, "AlkType:Uint16", 65535); +define_uint_validator!(Uint32Validator, uint32_factory, "AlkType:Uint32", 4294967295); // Int64/Uint64 use the full i64/u64 range, so the macro's `n <= $max` check // is always true (clippy: "comparison useless due to type limits"). Write @@ -88,7 +88,7 @@ fn int64_factory<'a>( if value.as_bool() == Some(true) { Ok(Box::new(Int64Validator)) } else { - Err(ValidationError::schema("TypeDef:Int64 must be set to true")) + Err(ValidationError::schema("AlkType:Int64 must be set to true")) } } @@ -113,19 +113,19 @@ fn uint64_factory<'a>( if value.as_bool() == Some(true) { Ok(Box::new(Uint64Validator)) } else { - Err(ValidationError::schema("TypeDef:Uint64 must be set to true")) + Err(ValidationError::schema("AlkType:Uint64 must be set to true")) } } define_float_validator!( Float32Validator, float32_factory, - "TypeDef:Float32", + "AlkType:Float32", "expected a finite f32-compatible number" ); define_float_validator!( Float64Validator, float64_factory, - "TypeDef:Float64", + "AlkType:Float64", "expected a finite f64 number" ); @@ -187,7 +187,7 @@ impl Keyword for BytesValidator { } } -/// `TypeDef:Enum` is a layout marker — the built-in `enum` keyword +/// `AlkType:Enum` is a layout marker — the built-in `enum` keyword /// handles value-membership validation. The custom keyword exists solely /// for the layout engine to recognize the type as a fixed-size u32 index. struct EnumValidator; @@ -257,11 +257,11 @@ fn is_rfc3339_timestamp(s: &str) -> bool { // Composite validators (generated via macros) // --------------------------------------------------------------------------- -define_type_validator!(StructValidator, struct_factory, "TypeDef:Struct", is_object, "expected an object"); -define_type_validator!(UnionValidator, union_factory, "TypeDef:Union", is_object, "expected an object for union"); -define_type_validator!(ArrayValidator, array_factory, "TypeDef:Array", is_array, "expected an array"); -define_type_validator!(RecordValidator, record_factory, "TypeDef:Record", is_object, "expected an object for record"); -define_type_validator!(BooleanValidator, boolean_factory, "TypeDef:Boolean", is_boolean, "expected a boolean"); +define_type_validator!(StructValidator, struct_factory, "AlkType:Struct", is_object, "expected an object"); +define_type_validator!(UnionValidator, union_factory, "AlkType:Union", is_object, "expected an object for union"); +define_type_validator!(ArrayValidator, array_factory, "AlkType:Array", is_array, "expected an array"); +define_type_validator!(RecordValidator, record_factory, "AlkType:Record", is_object, "expected an object for record"); +define_type_validator!(BooleanValidator, boolean_factory, "AlkType:Boolean", is_boolean, "expected a boolean"); // --------------------------------------------------------------------------- // Factory closures for non-macro-generated validators @@ -274,7 +274,7 @@ fn string_factory<'a>( ) -> Result, ValidationError<'a>> { if !value.is_boolean() && !value.is_object() { return Err(ValidationError::schema( - "TypeDef:String must be set to true or an annotation object", + "AlkType:String must be set to true or an annotation object", )); } let max_length = parent @@ -291,7 +291,7 @@ fn bytes_factory<'a>( ) -> Result, ValidationError<'a>> { if !value.is_boolean() && !value.is_object() { return Err(ValidationError::schema( - "TypeDef:Bytes must be set to true or an annotation object", + "AlkType:Bytes must be set to true or an annotation object", )); } let max_length = parent @@ -309,7 +309,7 @@ fn enum_factory<'a>( if value.as_bool() == Some(true) { Ok(Box::new(EnumValidator)) } else { - Err(ValidationError::schema("TypeDef:Enum must be set to true")) + Err(ValidationError::schema("AlkType:Enum must be set to true")) } } @@ -322,7 +322,7 @@ fn timestamp_factory<'a>( Ok(Box::new(TimestampValidator)) } else { Err(ValidationError::schema( - "TypeDef:Timestamp must be set to true", + "AlkType:Timestamp must be set to true", )) } } @@ -339,13 +339,13 @@ mod tests { #[test] fn validates_valid_struct_instance() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "type": "object", "properties": { - "id": { "TypeDef:Uint32": true, "type": "integer" }, - "score": { "TypeDef:Float32": true, "type": "number" }, - "flag": { "TypeDef:Uint8": true, "type": "integer" }, - "count": { "TypeDef:Uint16": true, "type": "integer" } + "id": { "AlkType:Uint32": true, "type": "integer" }, + "score": { "AlkType:Float32": true, "type": "number" }, + "flag": { "AlkType:Uint8": true, "type": "integer" }, + "count": { "AlkType:Uint16": true, "type": "integer" } }, "required": ["id", "score", "flag", "count"] }); @@ -362,9 +362,9 @@ mod tests { #[test] fn rejects_uint32_out_of_range() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "type": "object", - "properties": { "id": { "TypeDef:Uint32": true, "type": "integer" } }, + "properties": { "id": { "AlkType:Uint32": true, "type": "integer" } }, "required": ["id"] }); let validator = validator_for(&schema); @@ -375,9 +375,9 @@ mod tests { #[test] fn validates_int8_range() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "type": "object", - "properties": { "val": { "TypeDef:Int8": true, "type": "integer" } }, + "properties": { "val": { "AlkType:Int8": true, "type": "integer" } }, "required": ["val"] }); let validator = validator_for(&schema); @@ -391,11 +391,11 @@ mod tests { #[test] fn validates_int16_and_int32_ranges() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "type": "object", "properties": { - "i16": { "TypeDef:Int16": true, "type": "integer" }, - "i32": { "TypeDef:Int32": true, "type": "integer" } + "i16": { "AlkType:Int16": true, "type": "integer" }, + "i32": { "AlkType:Int32": true, "type": "integer" } }, "required": ["i16", "i32"] }); @@ -409,11 +409,11 @@ mod tests { #[test] fn validates_uint16_and_uint32_ranges() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "type": "object", "properties": { - "u16": { "TypeDef:Uint16": true, "type": "integer" }, - "u32": { "TypeDef:Uint32": true, "type": "integer" } + "u16": { "AlkType:Uint16": true, "type": "integer" }, + "u32": { "AlkType:Uint32": true, "type": "integer" } }, "required": ["u16", "u32"] }); @@ -426,9 +426,9 @@ mod tests { #[test] fn validates_int64_range() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "type": "object", - "properties": { "val": { "TypeDef:Int64": true, "type": "integer" } }, + "properties": { "val": { "AlkType:Int64": true, "type": "integer" } }, "required": ["val"] }); let validator = validator_for(&schema); @@ -441,9 +441,9 @@ mod tests { #[test] fn validates_uint64_range() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "type": "object", - "properties": { "val": { "TypeDef:Uint64": true, "type": "integer" } }, + "properties": { "val": { "AlkType:Uint64": true, "type": "integer" } }, "required": ["val"] }); let validator = validator_for(&schema); @@ -456,11 +456,11 @@ mod tests { #[test] fn validates_float_finiteness() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "type": "object", "properties": { - "f32": { "TypeDef:Float32": true, "type": "number" }, - "f64": { "TypeDef:Float64": true, "type": "number" } + "f32": { "AlkType:Float32": true, "type": "number" }, + "f64": { "AlkType:Float64": true, "type": "number" } }, "required": ["f32", "f64"] }); @@ -473,9 +473,9 @@ mod tests { #[test] fn validates_boolean() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "type": "object", - "properties": { "active": { "TypeDef:Boolean": true, "type": "boolean" } }, + "properties": { "active": { "AlkType:Boolean": true, "type": "boolean" } }, "required": ["active"] }); let validator = validator_for(&schema); @@ -487,10 +487,10 @@ mod tests { #[test] fn validates_string_and_maxlength() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "type": "object", "properties": { - "name": { "TypeDef:String": true, "type": "string", "maxLength": 5 } + "name": { "AlkType:String": true, "type": "string", "maxLength": 5 } }, "required": ["name"] }); @@ -504,10 +504,10 @@ mod tests { #[test] fn validates_bytes_and_maxlength() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "type": "object", "properties": { - "blob": { "TypeDef:Bytes": true, "type": "string", "maxLength": 4 } + "blob": { "AlkType:Bytes": true, "type": "string", "maxLength": 4 } }, "required": ["blob"] }); @@ -520,11 +520,11 @@ mod tests { #[test] fn enum_validator_is_noop_and_builtin_enum_handles_membership() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "type": "object", "properties": { "status": { - "TypeDef:Enum": true, + "AlkType:Enum": true, "type": "string", "enum": ["ok", "error", "pending"] } @@ -540,10 +540,10 @@ mod tests { #[test] fn validates_timestamp_rfc3339() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "type": "object", "properties": { - "created_at": { "TypeDef:Timestamp": true, "type": "string" } + "created_at": { "AlkType:Timestamp": true, "type": "string" } }, "required": ["created_at"] }); @@ -557,13 +557,13 @@ mod tests { #[test] fn validates_array_type() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "type": "object", "properties": { "items": { - "TypeDef:Array": true, + "AlkType:Array": true, "type": "array", - "items": { "TypeDef:Uint8": true, "type": "integer" } + "items": { "AlkType:Uint8": true, "type": "integer" } } }, "required": ["items"] @@ -576,13 +576,13 @@ mod tests { #[test] fn validates_record_type() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "type": "object", "properties": { "counts": { - "TypeDef:Record": true, + "AlkType:Record": true, "type": "object", - "additionalProperties": { "TypeDef:Uint32": true, "type": "integer" } + "additionalProperties": { "AlkType:Uint32": true, "type": "integer" } } }, "required": ["counts"] @@ -595,11 +595,11 @@ mod tests { #[test] fn validates_union_type() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "type": "object", "properties": { "packet": { - "TypeDef:Union": true, + "AlkType:Union": true, "type": "object", "properties": { "type": { "type": "string" } @@ -616,15 +616,15 @@ mod tests { #[test] fn build_validator_returns_schema_error_for_malformed_keyword() { - let schema = json!({"TypeDef:Uint32": "not-a-bool"}); + let schema = json!({"AlkType:Uint32": "not-a-bool"}); let err = build_validator(&schema).expect_err("should fail"); - assert!(matches!(err, TypedefError::Schema(_)), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Schema(_)), "got {err:?}"); } #[test] - fn build_validator_maps_build_error_to_typedef_error() { + fn build_validator_maps_build_error_to_alktype_error() { let schema = json!([1, 2, 3]); let err = build_validator(&schema).expect_err("schema must be an object"); - assert!(matches!(err, TypedefError::Schema(_)), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Schema(_)), "got {err:?}"); } } diff --git a/tests/engine_integration.rs b/tests/engine_integration.rs index 8d6da58..b1b988a 100644 --- a/tests/engine_integration.rs +++ b/tests/engine_integration.rs @@ -1,4 +1,4 @@ -//! Integration tests for the `TypedefEngine` public API. +//! Integration tests for the `AlkTypeEngine` public API. //! //! Exercises the engine across both layout modes, the convenience //! accessors, validation convenience methods, and the aligned-mode @@ -10,21 +10,21 @@ use serde_json::json; fn mixed_fixed_struct_schema() -> serde_json::Value { json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "endian": "little", "properties": { - "flag": { "TypeDef:Uint8": true }, - "id": { "TypeDef:Uint32": true }, - "score": { "TypeDef:Float32": true }, - "tag": { "TypeDef:String": true } + "flag": { "AlkType:Uint8": true }, + "id": { "AlkType:Uint32": true }, + "score": { "AlkType:Float32": true }, + "tag": { "AlkType:String": true } } }) } #[test] -fn compile_aligned_builds_engine_with_offset_map() -> Result<(), TypedefError> { +fn compile_aligned_builds_engine_with_offset_map() -> Result<(), AlkTypeError> { let mut schema = mixed_fixed_struct_schema(); - let engine = TypedefEngine::compile(&mut schema, LayoutMode::Aligned)?; + let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Aligned)?; assert_eq!(engine.mode(), LayoutMode::Aligned); assert!(engine.offset_map().is_some()); assert!(engine.layout_builder().is_none()); @@ -33,9 +33,9 @@ fn compile_aligned_builds_engine_with_offset_map() -> Result<(), TypedefError> { } #[test] -fn compile_packed_builds_engine_with_builder_and_reader() -> Result<(), TypedefError> { +fn compile_packed_builds_engine_with_builder_and_reader() -> Result<(), AlkTypeError> { let mut schema = mixed_fixed_struct_schema(); - let engine = TypedefEngine::compile(&mut schema, LayoutMode::Packed)?; + let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Packed)?; assert_eq!(engine.mode(), LayoutMode::Packed); assert!(engine.offset_map().is_none()); assert!(engine.layout_builder().is_some()); @@ -44,20 +44,20 @@ fn compile_packed_builds_engine_with_builder_and_reader() -> Result<(), TypedefE } #[test] -fn compile_normalizes_bare_name_refs() -> Result<(), TypedefError> { +fn compile_normalizes_bare_name_refs() -> Result<(), AlkTypeError> { let mut schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { "child": { "$ref": "Child" } }, "$defs": { "Child": { - "TypeDef:Struct": true, - "properties": { "x": { "TypeDef:Uint8": true } } + "AlkType:Struct": true, + "properties": { "x": { "AlkType:Uint8": true } } } } }); - let _engine = TypedefEngine::compile(&mut schema, LayoutMode::Packed)?; + let _engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Packed)?; assert_eq!( schema["properties"]["child"]["$ref"], json!("#/$defs/Child") @@ -66,20 +66,20 @@ fn compile_normalizes_bare_name_refs() -> Result<(), TypedefError> { } #[test] -fn compile_leaves_full_pointer_refs_unchanged() -> Result<(), TypedefError> { +fn compile_leaves_full_pointer_refs_unchanged() -> Result<(), AlkTypeError> { let mut schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { "child": { "$ref": "#/$defs/Child" } }, "$defs": { "Child": { - "TypeDef:Struct": true, - "properties": { "x": { "TypeDef:Uint8": true } } + "AlkType:Struct": true, + "properties": { "x": { "AlkType:Uint8": true } } } } }); - let _engine = TypedefEngine::compile(&mut schema, LayoutMode::Packed)?; + let _engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Packed)?; assert_eq!( schema["properties"]["child"]["$ref"], json!("#/$defs/Child") @@ -88,103 +88,103 @@ fn compile_leaves_full_pointer_refs_unchanged() -> Result<(), TypedefError> { } #[test] -fn compile_returns_schema_error_when_no_typedef_kind() { +fn compile_returns_schema_error_when_no_alktype_kind() { let mut schema = json!({ "type": "object", "properties": {} }); - let err = TypedefEngine::compile(&mut schema, LayoutMode::Aligned).unwrap_err(); - assert!(matches!(err, TypedefError::Schema(_)), "got {err:?}"); + let err = AlkTypeEngine::compile(&mut schema, LayoutMode::Aligned).unwrap_err(); + assert!(matches!(err, AlkTypeError::Schema(_)), "got {err:?}"); } #[test] -fn endian_parsed_from_schema_big() -> Result<(), TypedefError> { +fn endian_parsed_from_schema_big() -> Result<(), AlkTypeError> { let mut schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "endian": "big", - "properties": { "id": { "TypeDef:Uint32": true } } + "properties": { "id": { "AlkType:Uint32": true } } }); - let engine = TypedefEngine::compile(&mut schema, LayoutMode::Packed)?; + let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Packed)?; assert_eq!(engine.endian(), Endian::Big); Ok(()) } #[test] -fn endian_defaults_to_little() -> Result<(), TypedefError> { +fn endian_defaults_to_little() -> Result<(), AlkTypeError> { let mut schema = json!({ - "TypeDef:Struct": true, - "properties": { "id": { "TypeDef:Uint32": true } } + "AlkType:Struct": true, + "properties": { "id": { "AlkType:Uint32": true } } }); - let engine = TypedefEngine::compile(&mut schema, LayoutMode::Packed)?; + let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Packed)?; assert_eq!(engine.endian(), Endian::Little); Ok(()) } #[test] -fn validate_json_accepts_valid_instance() -> Result<(), TypedefError> { +fn validate_json_accepts_valid_instance() -> Result<(), AlkTypeError> { let mut schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "type": "object", "properties": { - "id": { "TypeDef:Uint32": true, "type": "integer" } + "id": { "AlkType:Uint32": true, "type": "integer" } }, "required": ["id"] }); - let engine = TypedefEngine::compile(&mut schema, LayoutMode::Aligned)?; + let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Aligned)?; assert!(engine.validate_json(&json!({"id": 42})).is_ok()); Ok(()) } #[test] -fn validate_json_rejects_invalid_instance() -> Result<(), TypedefError> { +fn validate_json_rejects_invalid_instance() -> Result<(), AlkTypeError> { let mut schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "type": "object", "properties": { - "id": { "TypeDef:Uint32": true, "type": "integer" } + "id": { "AlkType:Uint32": true, "type": "integer" } }, "required": ["id"] }); - let engine = TypedefEngine::compile(&mut schema, LayoutMode::Aligned)?; + let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Aligned)?; let err = engine.validate_json(&json!({"id": -1})).unwrap_err(); - assert!(matches!(err, TypedefError::Validation(_)), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Validation(_)), "got {err:?}"); Ok(()) } #[test] -fn is_valid_json_returns_bool() -> Result<(), TypedefError> { +fn is_valid_json_returns_bool() -> Result<(), AlkTypeError> { let mut schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "type": "object", "properties": { - "id": { "TypeDef:Uint32": true, "type": "integer" } + "id": { "AlkType:Uint32": true, "type": "integer" } }, "required": ["id"] }); - let engine = TypedefEngine::compile(&mut schema, LayoutMode::Aligned)?; + let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Aligned)?; assert!(engine.is_valid_json(&json!({"id": 42}))); assert!(!engine.is_valid_json(&json!({"id": -1}))); Ok(()) } #[test] -fn read_write_aligned_round_trips_all_fixed_size_kinds() -> Result<(), TypedefError> { +fn read_write_aligned_round_trips_all_fixed_size_kinds() -> Result<(), AlkTypeError> { let mut schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "endian": "little", "properties": { - "i8": { "TypeDef:Int8": true }, - "u8": { "TypeDef:Uint8": true }, - "i16": { "TypeDef:Int16": true }, - "u16": { "TypeDef:Uint16": true }, - "i32": { "TypeDef:Int32": true }, - "u32": { "TypeDef:Uint32": true }, - "i64": { "TypeDef:Int64": true }, - "u64": { "TypeDef:Uint64": true }, - "f32": { "TypeDef:Float32": true }, - "f64": { "TypeDef:Float64": true }, - "b": { "TypeDef:Boolean": true }, - "e": { "TypeDef:Enum": true } + "i8": { "AlkType:Int8": true }, + "u8": { "AlkType:Uint8": true }, + "i16": { "AlkType:Int16": true }, + "u16": { "AlkType:Uint16": true }, + "i32": { "AlkType:Int32": true }, + "u32": { "AlkType:Uint32": true }, + "i64": { "AlkType:Int64": true }, + "u64": { "AlkType:Uint64": true }, + "f32": { "AlkType:Float32": true }, + "f64": { "AlkType:Float64": true }, + "b": { "AlkType:Boolean": true }, + "e": { "AlkType:Enum": true } } }); - let engine = TypedefEngine::compile(&mut schema, LayoutMode::Aligned)?; + let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Aligned)?; let offset_map = engine.offset_map().expect("aligned mode has offset_map"); let mut buffer = vec![0u8; offset_map.total_size()]; @@ -229,14 +229,14 @@ fn read_write_aligned_round_trips_all_fixed_size_kinds() -> Result<(), TypedefEr } #[test] -fn read_write_aligned_round_trips_string() -> Result<(), TypedefError> { +fn read_write_aligned_round_trips_string() -> Result<(), AlkTypeError> { let mut schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "name": { "TypeDef:String": true } + "name": { "AlkType:String": true } } }); - let engine = TypedefEngine::compile(&mut schema, LayoutMode::Aligned)?; + let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Aligned)?; let offset_map = engine.offset_map().expect("aligned mode has offset_map"); let mut buffer = vec![0u8; offset_map.total_size() + 64]; engine.write_field(&mut buffer, "name", &FieldValue::String("hello world"))?; @@ -248,14 +248,14 @@ fn read_write_aligned_round_trips_string() -> Result<(), TypedefError> { } #[test] -fn read_write_aligned_round_trips_bytes() -> Result<(), TypedefError> { +fn read_write_aligned_round_trips_bytes() -> Result<(), AlkTypeError> { let mut schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "blob": { "TypeDef:Bytes": true } + "blob": { "AlkType:Bytes": true } } }); - let engine = TypedefEngine::compile(&mut schema, LayoutMode::Aligned)?; + let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Aligned)?; let offset_map = engine.offset_map().expect("aligned mode has offset_map"); let payload = b"the quick brown fox".to_vec(); let mut buffer = vec![0u8; offset_map.total_size() + payload.len()]; @@ -268,109 +268,109 @@ fn read_write_aligned_round_trips_bytes() -> Result<(), TypedefError> { } #[test] -fn read_field_returns_access_error_in_packed_mode() -> Result<(), TypedefError> { +fn read_field_returns_access_error_in_packed_mode() -> Result<(), AlkTypeError> { let mut schema = json!({ - "TypeDef:Struct": true, - "properties": { "id": { "TypeDef:Uint32": true } } + "AlkType:Struct": true, + "properties": { "id": { "AlkType:Uint32": true } } }); - let engine = TypedefEngine::compile(&mut schema, LayoutMode::Packed)?; + let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Packed)?; let buffer = [0u8; 4]; let err = engine.read_field(&buffer, "id").unwrap_err(); - assert!(matches!(err, TypedefError::Access { .. }), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Access { .. }), "got {err:?}"); Ok(()) } #[test] -fn write_field_returns_access_error_in_packed_mode() -> Result<(), TypedefError> { +fn write_field_returns_access_error_in_packed_mode() -> Result<(), AlkTypeError> { let mut schema = json!({ - "TypeDef:Struct": true, - "properties": { "id": { "TypeDef:Uint32": true } } + "AlkType:Struct": true, + "properties": { "id": { "AlkType:Uint32": true } } }); - let engine = TypedefEngine::compile(&mut schema, LayoutMode::Packed)?; + let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Packed)?; let mut buffer = [0u8; 4]; let err = engine .write_field(&mut buffer, "id", &FieldValue::U32(1)) .unwrap_err(); - assert!(matches!(err, TypedefError::Access { .. }), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Access { .. }), "got {err:?}"); Ok(()) } #[test] -fn read_field_returns_offset_error_for_missing_path() -> Result<(), TypedefError> { +fn read_field_returns_offset_error_for_missing_path() -> Result<(), AlkTypeError> { let mut schema = json!({ - "TypeDef:Struct": true, - "properties": { "id": { "TypeDef:Uint32": true } } + "AlkType:Struct": true, + "properties": { "id": { "AlkType:Uint32": true } } }); - let engine = TypedefEngine::compile(&mut schema, LayoutMode::Aligned)?; + let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Aligned)?; let buffer = [0u8; 8]; let err = engine.read_field(&buffer, "missing").unwrap_err(); - assert!(matches!(err, TypedefError::Offset { .. }), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Offset { .. }), "got {err:?}"); Ok(()) } #[test] -fn write_field_returns_offset_error_for_missing_path() -> Result<(), TypedefError> { +fn write_field_returns_offset_error_for_missing_path() -> Result<(), AlkTypeError> { let mut schema = json!({ - "TypeDef:Struct": true, - "properties": { "id": { "TypeDef:Uint32": true } } + "AlkType:Struct": true, + "properties": { "id": { "AlkType:Uint32": true } } }); - let engine = TypedefEngine::compile(&mut schema, LayoutMode::Aligned)?; + let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Aligned)?; let mut buffer = [0u8; 8]; let err = engine .write_field(&mut buffer, "missing", &FieldValue::U32(1)) .unwrap_err(); - assert!(matches!(err, TypedefError::Offset { .. }), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Offset { .. }), "got {err:?}"); Ok(()) } #[test] -fn read_field_returns_access_error_for_composite_types() -> Result<(), TypedefError> { +fn read_field_returns_access_error_for_composite_types() -> Result<(), AlkTypeError> { let mut schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { "vals": { - "TypeDef:Array": true, - "items": { "TypeDef:Uint32": true } + "AlkType:Array": true, + "items": { "AlkType:Uint32": true } } } }); - let engine = TypedefEngine::compile(&mut schema, LayoutMode::Aligned)?; + let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Aligned)?; let buffer = [0u8; 8]; let err = engine.read_field(&buffer, "vals").unwrap_err(); - assert!(matches!(err, TypedefError::Access { .. }), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Access { .. }), "got {err:?}"); Ok(()) } #[test] -fn write_field_returns_access_error_for_composite_value() -> Result<(), TypedefError> { +fn write_field_returns_access_error_for_composite_value() -> Result<(), AlkTypeError> { let mut schema = json!({ - "TypeDef:Struct": true, - "properties": { "id": { "TypeDef:Uint32": true } } + "AlkType:Struct": true, + "properties": { "id": { "AlkType:Uint32": true } } }); - let engine = TypedefEngine::compile(&mut schema, LayoutMode::Aligned)?; + let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Aligned)?; let mut buffer = [0u8; 8]; let err = engine .write_field(&mut buffer, "id", &FieldValue::Struct { start: 0, end: 4 }) .unwrap_err(); - assert!(matches!(err, TypedefError::Access { .. }), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Access { .. }), "got {err:?}"); Ok(()) } #[test] -fn read_field_aligned_reads_nested_struct_byte_range() -> Result<(), TypedefError> { +fn read_field_aligned_reads_nested_struct_byte_range() -> Result<(), AlkTypeError> { let mut schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { "header": { - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "version": { "TypeDef:Uint8": true }, - "magic": { "TypeDef:Uint32": true } + "version": { "AlkType:Uint8": true }, + "magic": { "AlkType:Uint32": true } } } } }); - let engine = TypedefEngine::compile(&mut schema, LayoutMode::Aligned)?; + let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Aligned)?; let offset_map = engine.offset_map().expect("aligned mode"); let mut buffer = vec![0u8; offset_map.total_size()]; diff --git a/tests/error_paths.rs b/tests/error_paths.rs index 469e4c0..5b65927 100644 --- a/tests/error_paths.rs +++ b/tests/error_paths.rs @@ -1,8 +1,8 @@ //! Error path integration tests for `alktype`. //! -//! Exercises the `TypedefError` variants across the crate: +//! Exercises the `AlkTypeError` variants across the crate: //! `Access` (buffer too short, invalid UTF-8, invalid boolean byte, -//! unknown discriminator value), `Schema` (missing TypeDef kind, +//! unknown discriminator value), `Schema` (missing AlkType kind, //! malformed discriminator annotation), and `Offset` (missing //! variable-length field size in `LayoutBuilder::build`). @@ -17,7 +17,7 @@ fn read_u32_buffer_too_short_returns_access_error() { let buffer = [0u8; 2]; let err = data_access::read_u32(&buffer, 0, "header.id", Endian::Little).unwrap_err(); match err { - TypedefError::Access { field_path, reason } => { + AlkTypeError::Access { field_path, reason } => { assert_eq!(field_path, "header.id"); assert!(reason.contains("bounds"), "reason: {reason}"); } @@ -29,49 +29,49 @@ fn read_u32_buffer_too_short_returns_access_error() { fn read_u16_buffer_too_short_returns_access_error() { let buffer = [0u8; 1]; let err = data_access::read_u16(&buffer, 0, "tag", Endian::Little).unwrap_err(); - assert!(matches!(err, TypedefError::Access { .. }), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Access { .. }), "got {err:?}"); } #[test] fn read_u64_buffer_too_short_returns_access_error() { let buffer = [0u8; 4]; let err = data_access::read_u64(&buffer, 0, "offset", Endian::Big).unwrap_err(); - assert!(matches!(err, TypedefError::Access { .. }), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Access { .. }), "got {err:?}"); } #[test] fn read_f32_buffer_too_short_returns_access_error() { let buffer = [0u8; 2]; let err = data_access::read_f32(&buffer, 0, "score", Endian::Little).unwrap_err(); - assert!(matches!(err, TypedefError::Access { .. }), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Access { .. }), "got {err:?}"); } #[test] fn read_f64_buffer_too_short_returns_access_error() { let buffer = [0u8; 4]; let err = data_access::read_f64(&buffer, 0, "score", Endian::Little).unwrap_err(); - assert!(matches!(err, TypedefError::Access { .. }), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Access { .. }), "got {err:?}"); } #[test] fn read_i32_buffer_too_short_returns_access_error() { let buffer = [0u8; 2]; let err = data_access::read_i32(&buffer, 0, "id", Endian::Little).unwrap_err(); - assert!(matches!(err, TypedefError::Access { .. }), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Access { .. }), "got {err:?}"); } #[test] fn read_bool_buffer_too_short_returns_access_error() { let buffer: [u8; 0] = []; let err = data_access::read_bool(&buffer, 0, "flag").unwrap_err(); - assert!(matches!(err, TypedefError::Access { .. }), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Access { .. }), "got {err:?}"); } #[test] fn read_string_buffer_too_short_on_prefix_returns_access_error() { let buffer = [0u8; 2]; let err = data_access::read_string(&buffer, 0, "name", Endian::Little).unwrap_err(); - assert!(matches!(err, TypedefError::Access { .. }), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Access { .. }), "got {err:?}"); } #[test] @@ -79,7 +79,7 @@ fn read_string_buffer_too_short_on_data_returns_access_error() { let mut buffer = vec![0u8; 6]; buffer[0..4].copy_from_slice(&100u32.to_le_bytes()); let err = data_access::read_string(&buffer, 0, "name", Endian::Little).unwrap_err(); - assert!(matches!(err, TypedefError::Access { .. }), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Access { .. }), "got {err:?}"); } #[test] @@ -87,7 +87,7 @@ fn read_bytes_buffer_too_short_on_data_returns_access_error() { let mut buffer = vec![0u8; 5]; buffer[0..4].copy_from_slice(&100u32.to_le_bytes()); let err = data_access::read_bytes(&buffer, 0, "blob", Endian::Little).unwrap_err(); - assert!(matches!(err, TypedefError::Access { .. }), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Access { .. }), "got {err:?}"); } #[test] @@ -96,7 +96,7 @@ fn read_string_invalid_utf8_returns_access_error() { let invalid = [0xFFu8, 0xFE, 0xFD]; let _ = data_access::write_bytes(&mut buffer, 0, &invalid, "name", Endian::Little); let err = data_access::read_string(&buffer, 0, "name", Endian::Little).unwrap_err(); - assert!(matches!(err, TypedefError::Access { .. }), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Access { .. }), "got {err:?}"); } #[test] @@ -104,7 +104,7 @@ fn read_bool_invalid_byte_returns_access_error() { let buffer = [0x02u8]; let err = data_access::read_bool(&buffer, 0, "flag").unwrap_err(); match err { - TypedefError::Access { field_path, reason } => { + AlkTypeError::Access { field_path, reason } => { assert_eq!(field_path, "flag"); assert!(reason.contains("0x02"), "reason: {reason}"); } @@ -113,14 +113,14 @@ fn read_bool_invalid_byte_returns_access_error() { } #[test] -fn read_bool_zero_is_false() -> Result<(), TypedefError> { +fn read_bool_zero_is_false() -> Result<(), AlkTypeError> { let buffer = [0x00u8]; assert!(!data_access::read_bool(&buffer, 0, "flag")?); Ok(()) } #[test] -fn read_bool_one_is_true() -> Result<(), TypedefError> { +fn read_bool_one_is_true() -> Result<(), AlkTypeError> { let buffer = [0x01u8]; assert!(data_access::read_bool(&buffer, 0, "flag")?); Ok(()) @@ -130,14 +130,14 @@ fn read_bool_one_is_true() -> Result<(), TypedefError> { fn read_bool_three_is_access_error() { let buffer = [0x03u8]; let err = data_access::read_bool(&buffer, 0, "flag").unwrap_err(); - assert!(matches!(err, TypedefError::Access { .. }), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Access { .. }), "got {err:?}"); } #[test] fn write_u32_buffer_too_short_returns_access_error() { let mut buffer = [0u8; 2]; let err = data_access::write_u32(&mut buffer, 0, 1, "id", Endian::Little).unwrap_err(); - assert!(matches!(err, TypedefError::Access { .. }), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Access { .. }), "got {err:?}"); } #[test] @@ -145,136 +145,136 @@ fn write_string_buffer_too_short_returns_access_error() { let mut buffer = vec![0u8; 4]; let err = data_access::write_string(&mut buffer, 0, "hello", "name", Endian::Little).unwrap_err(); - assert!(matches!(err, TypedefError::Access { .. }), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Access { .. }), "got {err:?}"); } #[test] -fn compile_missing_typedef_kind_returns_schema_error() { +fn compile_missing_alktype_kind_returns_schema_error() { let mut schema = json!({ "type": "object", "properties": {} }); - let err = TypedefEngine::compile(&mut schema, LayoutMode::Aligned).unwrap_err(); - assert!(matches!(err, TypedefError::Schema(_)), "got {err:?}"); + let err = AlkTypeEngine::compile(&mut schema, LayoutMode::Aligned).unwrap_err(); + assert!(matches!(err, AlkTypeError::Schema(_)), "got {err:?}"); } #[test] -fn offset_map_compute_missing_typedef_kind_returns_schema_error() { +fn offset_map_compute_missing_alktype_kind_returns_schema_error() { let schema = json!({ "type": "object", "properties": {} }); let err = OffsetMap::compute(&schema).unwrap_err(); - assert!(matches!(err, TypedefError::Schema(_)), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Schema(_)), "got {err:?}"); } #[test] fn offset_map_compute_non_struct_top_level_returns_schema_error() { - let schema = json!({ "TypeDef:Uint32": true }); + let schema = json!({ "AlkType:Uint32": true }); let err = OffsetMap::compute(&schema).unwrap_err(); - assert!(matches!(err, TypedefError::Schema(_)), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Schema(_)), "got {err:?}"); } #[test] -fn layout_builder_new_missing_typedef_kind_returns_schema_error() { +fn layout_builder_new_missing_alktype_kind_returns_schema_error() { let schema = json!({ "type": "object", "properties": {} }); let err = LayoutBuilder::new(&schema).unwrap_err(); - assert!(matches!(err, TypedefError::Schema(_)), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Schema(_)), "got {err:?}"); } #[test] fn layout_builder_new_non_struct_top_level_returns_schema_error() { - let schema = json!({ "TypeDef:Uint32": true }); + let schema = json!({ "AlkType:Uint32": true }); let err = LayoutBuilder::new(&schema).unwrap_err(); - assert!(matches!(err, TypedefError::Schema(_)), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Schema(_)), "got {err:?}"); } #[test] fn parse_discriminator_missing_returns_schema_error() { - let schema = json!({"TypeDef:Union": true}); + let schema = json!({"AlkType:Union": true}); let err = parse_discriminator(&schema).unwrap_err(); - assert!(matches!(err, TypedefError::Schema(_)), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Schema(_)), "got {err:?}"); } #[test] fn parse_discriminator_field_missing_name_returns_schema_error() { let schema = json!({ - "TypeDef:Union": true, + "AlkType:Union": true, "discriminator": {"kind": "field"} }); let err = parse_discriminator(&schema).unwrap_err(); - assert!(matches!(err, TypedefError::Schema(_)), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Schema(_)), "got {err:?}"); } #[test] fn parse_discriminator_unknown_kind_returns_schema_error() { let schema = json!({ - "TypeDef:Union": true, + "AlkType:Union": true, "discriminator": {"kind": "magic"} }); let err = parse_discriminator(&schema).unwrap_err(); - assert!(matches!(err, TypedefError::Schema(_)), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Schema(_)), "got {err:?}"); } #[test] fn parse_discriminator_byte_invalid_type_returns_schema_error() { let schema = json!({ - "TypeDef:Union": true, - "discriminator": {"kind": "byte", "type": "TypeDef:Float32"} + "AlkType:Union": true, + "discriminator": {"kind": "byte", "type": "AlkType:Float32"} }); let err = parse_discriminator(&schema).unwrap_err(); - assert!(matches!(err, TypedefError::Schema(_)), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Schema(_)), "got {err:?}"); } #[test] -fn read_byte_discriminator_unknown_value_returns_access_error() -> Result<(), TypedefError> { +fn read_byte_discriminator_unknown_value_returns_access_error() -> Result<(), AlkTypeError> { let union_schema = json!({ - "TypeDef:Union": true, - "discriminator": {"kind": "byte", "type": "TypeDef:Uint8"}, - "mapping": {"5": {"TypeDef:Struct": true, "properties": {"x": {"TypeDef:Uint8": true}}}} + "AlkType:Union": true, + "discriminator": {"kind": "byte", "type": "AlkType:Uint8"}, + "mapping": {"5": {"AlkType:Struct": true, "properties": {"x": {"AlkType:Uint8": true}}}} }); let buffer = [99u8, 0x00, 0x00]; let err = tunion::read_byte_discriminator(&buffer, &union_schema, Endian::Little).unwrap_err(); - assert!(matches!(err, TypedefError::Access { .. }), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Access { .. }), "got {err:?}"); Ok(()) } #[test] -fn read_byte_discriminator_buffer_too_short_returns_access_error() -> Result<(), TypedefError> { +fn read_byte_discriminator_buffer_too_short_returns_access_error() -> Result<(), AlkTypeError> { let union_schema = json!({ - "TypeDef:Union": true, - "discriminator": {"kind": "byte", "offset": 4, "type": "TypeDef:Uint32"}, - "mapping": {"5": {"TypeDef:Struct": true, "properties": {"x": {"TypeDef:Uint8": true}}}} + "AlkType:Union": true, + "discriminator": {"kind": "byte", "offset": 4, "type": "AlkType:Uint32"}, + "mapping": {"5": {"AlkType:Struct": true, "properties": {"x": {"AlkType:Uint8": true}}}} }); let buffer = [0u8; 2]; let err = tunion::read_byte_discriminator(&buffer, &union_schema, Endian::Little).unwrap_err(); - assert!(matches!(err, TypedefError::Access { .. }), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Access { .. }), "got {err:?}"); Ok(()) } #[test] -fn read_field_discriminator_unknown_value_returns_access_error() -> Result<(), TypedefError> { +fn read_field_discriminator_unknown_value_returns_access_error() -> Result<(), AlkTypeError> { let union_schema = json!({ - "TypeDef:Union": true, + "AlkType:Union": true, "discriminator": {"kind": "field", "name": "type"}, - "properties": {"type": {"TypeDef:Uint8": true}}, - "mapping": {"0": {"TypeDef:Struct": true, "properties": {"x": {"TypeDef:Uint8": true}}}} + "properties": {"type": {"AlkType:Uint8": true}}, + "mapping": {"0": {"AlkType:Struct": true, "properties": {"x": {"AlkType:Uint8": true}}}} }); let mut buffer = vec![0u8; 8]; buffer[0] = 99; let err = tunion::read_field_discriminator(&buffer, &union_schema, 0, Endian::Little).unwrap_err(); - assert!(matches!(err, TypedefError::Access { .. }), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Access { .. }), "got {err:?}"); Ok(()) } #[test] fn layout_builder_missing_var_size_returns_offset_error() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "name": { "TypeDef:String": true } + "name": { "AlkType:String": true } } }); let builder = LayoutBuilder::new(&schema).expect("builder"); let empty: HashMap = HashMap::new(); let err = builder.build(&empty).unwrap_err(); match err { - TypedefError::Offset { field_path, reason } => { + AlkTypeError::Offset { field_path, reason } => { assert_eq!(field_path, "name"); assert!( reason.contains("missing variable-length field size"), @@ -288,54 +288,54 @@ fn layout_builder_missing_var_size_returns_offset_error() { #[test] fn layout_builder_missing_array_data_size_returns_offset_error() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { "vals": { - "TypeDef:Array": true, - "items": { "TypeDef:Uint32": true } + "AlkType:Array": true, + "items": { "AlkType:Uint32": true } } } }); let builder = LayoutBuilder::new(&schema).expect("builder"); let empty: HashMap = HashMap::new(); let err = builder.build(&empty).unwrap_err(); - assert!(matches!(err, TypedefError::Offset { .. }), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Offset { .. }), "got {err:?}"); } #[test] fn layout_builder_missing_discriminator_value_returns_offset_error() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { "payload": { - "TypeDef:Union": true, - "discriminator": {"kind": "byte", "type": "TypeDef:Uint8"}, + "AlkType:Union": true, + "discriminator": {"kind": "byte", "type": "AlkType:Uint8"}, "mapping": {"5": {"$ref": "#/$defs/Read"}} } }, "$defs": { - "Read": {"TypeDef:Struct": true, "properties": {"x": {"TypeDef:Uint8": true}}} + "Read": {"AlkType:Struct": true, "properties": {"x": {"AlkType:Uint8": true}}} } }); let builder = LayoutBuilder::new(&schema).expect("builder"); let empty: HashMap = HashMap::new(); let err = builder.build(&empty).unwrap_err(); - assert!(matches!(err, TypedefError::Offset { .. }), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Offset { .. }), "got {err:?}"); } #[test] fn layout_builder_unknown_discriminator_value_returns_offset_error() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { "payload": { - "TypeDef:Union": true, - "discriminator": {"kind": "byte", "type": "TypeDef:Uint8"}, + "AlkType:Union": true, + "discriminator": {"kind": "byte", "type": "AlkType:Uint8"}, "mapping": {"5": {"$ref": "#/$defs/Read"}} } }, "$defs": { - "Read": {"TypeDef:Struct": true, "properties": {"x": {"TypeDef:Uint8": true}}} + "Read": {"AlkType:Struct": true, "properties": {"x": {"AlkType:Uint8": true}}} } }); let builder = LayoutBuilder::new(&schema).expect("builder"); @@ -343,7 +343,7 @@ fn layout_builder_unknown_discriminator_value_returns_offset_error() { vs.insert("payload.__discriminator".to_string(), 99); let err = builder.build(&vs).unwrap_err(); match err { - TypedefError::Offset { reason, .. } => { + AlkTypeError::Offset { reason, .. } => { assert!(reason.contains("99"), "reason: {reason}"); } other => panic!("expected Offset, got {other:?}"), @@ -353,34 +353,34 @@ fn layout_builder_unknown_discriminator_value_returns_offset_error() { #[test] fn sequential_reader_buffer_too_short_returns_access_error() { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "id": { "TypeDef:Uint32": true } + "id": { "AlkType:Uint32": true } } }); let buffer = [0u8; 2]; let mut reader = SequentialReader::new(&schema).unwrap(); let err = reader.read_next(&buffer).unwrap_err(); - assert!(matches!(err, TypedefError::Access { .. }), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Access { .. }), "got {err:?}"); } #[test] fn sequential_reader_unknown_field_returns_schema_error() { let schema = json!({ - "TypeDef:Struct": true, - "properties": { "a": { "TypeDef:Uint8": true } } + "AlkType:Struct": true, + "properties": { "a": { "AlkType:Uint8": true } } }); let buffer = [0u8; 4]; let mut reader = SequentialReader::new(&schema).unwrap(); let err = reader.read_field(&buffer, "missing").unwrap_err(); - assert!(matches!(err, TypedefError::Schema(_)), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Schema(_)), "got {err:?}"); } #[test] fn sequential_reader_new_non_struct_returns_schema_error() { - let schema = json!({ "TypeDef:Uint32": true }); + let schema = json!({ "AlkType:Uint32": true }); let err = SequentialReader::new(&schema).unwrap_err(); - assert!(matches!(err, TypedefError::Schema(_)), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Schema(_)), "got {err:?}"); } #[test] @@ -391,7 +391,7 @@ fn read_string_indirect_data_region_too_short_returns_access_error() { let data_region = b"too short"; let err = data_access::read_bytes_indirect(&index, 0, data_region, "blob", Endian::Little) .unwrap_err(); - assert!(matches!(err, TypedefError::Access { .. }), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Access { .. }), "got {err:?}"); } #[test] @@ -400,7 +400,7 @@ fn read_bytes_indirect_index_too_short_returns_access_error() { let data_region = b"anything"; let err = data_access::read_bytes_indirect(&buffer, 0, data_region, "blob", Endian::Little) .unwrap_err(); - assert!(matches!(err, TypedefError::Access { .. }), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Access { .. }), "got {err:?}"); } #[test] @@ -411,5 +411,5 @@ fn read_string_indirect_invalid_utf8_returns_access_error() { let _ = data_access::write_u32(&mut index, 4, 3, "idx.len", Endian::Little); let err = data_access::read_string_indirect(&index, 0, data_region, "name", Endian::Little) .unwrap_err(); - assert!(matches!(err, TypedefError::Access { .. }), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Access { .. }), "got {err:?}"); } diff --git a/tests/poc_roundtrip.rs b/tests/poc_roundtrip.rs index c30fceb..8b73ff3 100644 --- a/tests/poc_roundtrip.rs +++ b/tests/poc_roundtrip.rs @@ -19,14 +19,14 @@ fn var_sizes(pairs: &[(&str, usize)]) -> HashMap { } #[test] -fn fixed_size_round_trip_via_offset_map() -> Result<(), TypedefError> { +fn fixed_size_round_trip_via_offset_map() -> Result<(), AlkTypeError> { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "id": { "TypeDef:Uint32": true }, - "score": { "TypeDef:Float32": true }, - "flag": { "TypeDef:Uint8": true }, - "count": { "TypeDef:Uint16": true } + "id": { "AlkType:Uint32": true }, + "score": { "AlkType:Float32": true }, + "flag": { "AlkType:Uint8": true }, + "count": { "AlkType:Uint16": true } } }); let offset_map = OffsetMap::compute(&schema)?; @@ -62,17 +62,17 @@ fn fixed_size_round_trip_via_offset_map() -> Result<(), TypedefError> { } #[test] -fn fixed_size_round_trip_via_engine_aligned() -> Result<(), TypedefError> { +fn fixed_size_round_trip_via_engine_aligned() -> Result<(), AlkTypeError> { let mut schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "endian": "little", "properties": { - "id": { "TypeDef:Uint32": true }, - "score": { "TypeDef:Float32": true }, - "flag": { "TypeDef:Uint8": true } + "id": { "AlkType:Uint32": true }, + "score": { "AlkType:Float32": true }, + "flag": { "AlkType:Uint8": true } } }); - let engine = TypedefEngine::compile(&mut schema, LayoutMode::Aligned)?; + let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Aligned)?; let offset_map = engine.offset_map().expect("aligned mode has offset_map"); let mut buffer = vec![0u8; offset_map.total_size()]; @@ -91,7 +91,7 @@ fn fixed_size_round_trip_via_engine_aligned() -> Result<(), TypedefError> { } #[test] -fn string_round_trip_via_data_access() -> Result<(), TypedefError> { +fn string_round_trip_via_data_access() -> Result<(), AlkTypeError> { let mut buffer = vec![0u8; 32]; let written = data_access::write_string(&mut buffer, 0, "hello", "name", Endian::Little)?; assert_eq!(written, 4 + 5); @@ -105,14 +105,14 @@ fn string_round_trip_via_data_access() -> Result<(), TypedefError> { } #[test] -fn string_round_trip_via_engine_aligned() -> Result<(), TypedefError> { +fn string_round_trip_via_engine_aligned() -> Result<(), AlkTypeError> { let mut schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "name": { "TypeDef:String": true } + "name": { "AlkType:String": true } } }); - let engine = TypedefEngine::compile(&mut schema, LayoutMode::Aligned)?; + let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Aligned)?; let offset_map = engine.offset_map().expect("aligned mode has offset_map"); let mut buffer = vec![0u8; offset_map.total_size() + 64]; engine.write_field(&mut buffer, "name", &FieldValue::String("hello"))?; @@ -124,7 +124,7 @@ fn string_round_trip_via_engine_aligned() -> Result<(), TypedefError> { } #[test] -fn bytes_round_trip_via_data_access() -> Result<(), TypedefError> { +fn bytes_round_trip_via_data_access() -> Result<(), AlkTypeError> { let payload = [0xAA, 0xBB, 0xCC, 0xDD]; let mut buffer = vec![0u8; 32]; let written = data_access::write_bytes(&mut buffer, 0, &payload, "data", Endian::Little)?; @@ -139,18 +139,18 @@ fn bytes_round_trip_via_data_access() -> Result<(), TypedefError> { } #[test] -fn nested_struct_round_trip_via_offset_map() -> Result<(), TypedefError> { +fn nested_struct_round_trip_via_offset_map() -> Result<(), AlkTypeError> { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { "header": { - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "version": { "TypeDef:Uint32": true }, - "magic": { "TypeDef:Uint32": true } + "version": { "AlkType:Uint32": true }, + "magic": { "AlkType:Uint32": true } } }, - "payload": { "TypeDef:Bytes": true } + "payload": { "AlkType:Bytes": true } } }); let offset_map = OffsetMap::compute(&schema)?; @@ -208,21 +208,21 @@ fn nested_struct_round_trip_via_offset_map() -> Result<(), TypedefError> { } #[test] -fn nested_struct_round_trip_via_engine_aligned() -> Result<(), TypedefError> { +fn nested_struct_round_trip_via_engine_aligned() -> Result<(), AlkTypeError> { let mut schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { "header": { - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "version": { "TypeDef:Uint8": true }, - "flags": { "TypeDef:Uint8": true } + "version": { "AlkType:Uint8": true }, + "flags": { "AlkType:Uint8": true } } }, - "payload_len": { "TypeDef:Uint32": true } + "payload_len": { "AlkType:Uint32": true } } }); - let engine = TypedefEngine::compile(&mut schema, LayoutMode::Aligned)?; + let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Aligned)?; let offset_map = engine.offset_map().expect("aligned mode"); assert_eq!(offset_map.get("header.version").unwrap().start, 0); @@ -250,13 +250,13 @@ fn nested_struct_round_trip_via_engine_aligned() -> Result<(), TypedefError> { } #[test] -fn big_endian_round_trip_via_offset_map() -> Result<(), TypedefError> { +fn big_endian_round_trip_via_offset_map() -> Result<(), AlkTypeError> { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "endian": "big", "properties": { - "id": { "TypeDef:Uint32": true }, - "offset": { "TypeDef:Float64": true } + "id": { "AlkType:Uint32": true }, + "offset": { "AlkType:Float64": true } } }); let offset_map = OffsetMap::compute(&schema)?; @@ -288,12 +288,12 @@ fn big_endian_round_trip_via_offset_map() -> Result<(), TypedefError> { } #[test] -fn alignment_padding_round_trip_u8_then_u32() -> Result<(), TypedefError> { +fn alignment_padding_round_trip_u8_then_u32() -> Result<(), AlkTypeError> { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "flag": { "TypeDef:Uint8": true }, - "id": { "TypeDef:Uint32": true } + "flag": { "AlkType:Uint8": true }, + "id": { "AlkType:Uint32": true } } }); let offset_map = OffsetMap::compute(&schema)?; @@ -333,14 +333,14 @@ fn alignment_padding_round_trip_u8_then_u32() -> Result<(), TypedefError> { } #[test] -fn packed_layout_round_trip_via_layout_builder() -> Result<(), TypedefError> { +fn packed_layout_round_trip_via_layout_builder() -> Result<(), AlkTypeError> { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "endian": "little", "properties": { - "flag": { "TypeDef:Uint8": true }, - "id": { "TypeDef:Uint32": true }, - "payload": { "TypeDef:String": true } + "flag": { "AlkType:Uint8": true }, + "id": { "AlkType:Uint32": true }, + "payload": { "AlkType:String": true } } }); let builder = LayoutBuilder::new(&schema)?; @@ -390,14 +390,14 @@ fn packed_layout_round_trip_via_layout_builder() -> Result<(), TypedefError> { } #[test] -fn sequential_reader_round_trip_packed_buffer() -> Result<(), TypedefError> { +fn sequential_reader_round_trip_packed_buffer() -> Result<(), AlkTypeError> { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "endian": "little", "properties": { - "id": { "TypeDef:Uint8": true }, - "name": { "TypeDef:String": true }, - "tail": { "TypeDef:Uint8": true } + "id": { "AlkType:Uint8": true }, + "name": { "AlkType:String": true }, + "tail": { "AlkType:Uint8": true } } }); let builder = LayoutBuilder::new(&schema)?; @@ -434,14 +434,14 @@ fn sequential_reader_round_trip_packed_buffer() -> Result<(), TypedefError> { } #[test] -fn sequential_reader_read_field_walks_preceding_fields() -> Result<(), TypedefError> { +fn sequential_reader_read_field_walks_preceding_fields() -> Result<(), AlkTypeError> { let schema = json!({ - "TypeDef:Struct": true, + "AlkType:Struct": true, "endian": "little", "properties": { - "a": { "TypeDef:Uint8": true }, - "b": { "TypeDef:Uint32": true }, - "c": { "TypeDef:Uint8": true } + "a": { "AlkType:Uint8": true }, + "b": { "AlkType:Uint32": true }, + "c": { "AlkType:Uint8": true } } }); let mut buffer = vec![0u8; 16]; @@ -461,13 +461,13 @@ fn sequential_reader_read_field_walks_preceding_fields() -> Result<(), TypedefEr } #[test] -fn tunion_byte_offset_discriminator_dispatch() -> Result<(), TypedefError> { +fn tunion_byte_offset_discriminator_dispatch() -> Result<(), AlkTypeError> { let union_schema = json!({ - "TypeDef:Union": true, + "AlkType:Union": true, "discriminator": { "kind": "byte", "offset": 0, - "type": "TypeDef:Uint8" + "type": "AlkType:Uint8" }, "mapping": { "5": { "$ref": "#/$defs/Read" }, @@ -475,18 +475,18 @@ fn tunion_byte_offset_discriminator_dispatch() -> Result<(), TypedefError> { }, "$defs": { "Read": { - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "handle": { "TypeDef:Uint32": true }, - "length": { "TypeDef:Uint32": true } + "handle": { "AlkType:Uint32": true }, + "length": { "AlkType:Uint32": true } } }, "Write": { - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "handle": { "TypeDef:Uint32": true }, - "length": { "TypeDef:Uint32": true }, - "data": { "TypeDef:Uint32": true } + "handle": { "AlkType:Uint32": true }, + "length": { "AlkType:Uint32": true }, + "data": { "AlkType:Uint32": true } } } } @@ -504,7 +504,7 @@ fn tunion_byte_offset_discriminator_dispatch() -> Result<(), TypedefError> { let variant = tunion::resolve_variant(&union_schema, &dispatch.key)?; assert_eq!( variant - .get("TypeDef:Struct") + .get("AlkType:Struct") .and_then(serde_json::Value::as_bool), Some(true) ); @@ -512,20 +512,20 @@ fn tunion_byte_offset_discriminator_dispatch() -> Result<(), TypedefError> { } #[test] -fn tunion_byte_offset_discriminator_size_lookup() -> Result<(), TypedefError> { +fn tunion_byte_offset_discriminator_size_lookup() -> Result<(), AlkTypeError> { let u8_schema = json!({ - "TypeDef:Union": true, - "discriminator": {"kind": "byte", "type": "TypeDef:Uint8"}, + "AlkType:Union": true, + "discriminator": {"kind": "byte", "type": "AlkType:Uint8"}, "mapping": {} }); let u16_schema = json!({ - "TypeDef:Union": true, - "discriminator": {"kind": "byte", "type": "TypeDef:Uint16"}, + "AlkType:Union": true, + "discriminator": {"kind": "byte", "type": "AlkType:Uint16"}, "mapping": {} }); let u32_schema = json!({ - "TypeDef:Union": true, - "discriminator": {"kind": "byte", "type": "TypeDef:Uint32"}, + "AlkType:Union": true, + "discriminator": {"kind": "byte", "type": "AlkType:Uint32"}, "mapping": {} }); assert_eq!(tunion::discriminator_size(&u8_schema)?, 1); diff --git a/tests/tunion_dispatch.rs b/tests/tunion_dispatch.rs index e11e229..cf65fe1 100644 --- a/tests/tunion_dispatch.rs +++ b/tests/tunion_dispatch.rs @@ -9,16 +9,16 @@ use alktype::data_access; use alktype::tunion; -use alktype::{Endian, TypedefError}; +use alktype::{Endian, AlkTypeError}; use serde_json::json; fn sftp_like_byte_union() -> serde_json::Value { json!({ - "TypeDef:Union": true, + "AlkType:Union": true, "discriminator": { "kind": "byte", "offset": 0, - "type": "TypeDef:Uint8" + "type": "AlkType:Uint8" }, "mapping": { "5": { "$ref": "#/$defs/Read" }, @@ -26,18 +26,18 @@ fn sftp_like_byte_union() -> serde_json::Value { }, "$defs": { "Read": { - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "handle": { "TypeDef:Uint32": true }, - "length": { "TypeDef:Uint32": true } + "handle": { "AlkType:Uint32": true }, + "length": { "AlkType:Uint32": true } } }, "Write": { - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "handle": { "TypeDef:Uint32": true }, - "length": { "TypeDef:Uint32": true }, - "data": { "TypeDef:Uint32": true } + "handle": { "AlkType:Uint32": true }, + "length": { "AlkType:Uint32": true }, + "data": { "AlkType:Uint32": true } } } } @@ -45,7 +45,7 @@ fn sftp_like_byte_union() -> serde_json::Value { } #[test] -fn read_byte_discriminator_uint8_dispatches_to_read() -> Result<(), TypedefError> { +fn read_byte_discriminator_uint8_dispatches_to_read() -> Result<(), AlkTypeError> { let union_schema = sftp_like_byte_union(); let mut buffer = vec![0u8; 16]; buffer[0] = 5; @@ -59,7 +59,7 @@ fn read_byte_discriminator_uint8_dispatches_to_read() -> Result<(), TypedefError let variant = tunion::resolve_variant(&union_schema, &dispatch.key)?; assert_eq!( variant - .get("TypeDef:Struct") + .get("AlkType:Struct") .and_then(serde_json::Value::as_bool), Some(true) ); @@ -67,7 +67,7 @@ fn read_byte_discriminator_uint8_dispatches_to_read() -> Result<(), TypedefError } #[test] -fn read_byte_discriminator_uint8_dispatches_to_write() -> Result<(), TypedefError> { +fn read_byte_discriminator_uint8_dispatches_to_write() -> Result<(), AlkTypeError> { let union_schema = sftp_like_byte_union(); let mut buffer = vec![0u8; 16]; buffer[0] = 6; @@ -88,16 +88,16 @@ fn read_byte_discriminator_uint8_dispatches_to_write() -> Result<(), TypedefErro } #[test] -fn read_byte_discriminator_uint16_little_endian() -> Result<(), TypedefError> { +fn read_byte_discriminator_uint16_little_endian() -> Result<(), AlkTypeError> { let schema = json!({ - "TypeDef:Union": true, + "AlkType:Union": true, "discriminator": { "kind": "byte", "offset": 2, - "type": "TypeDef:Uint16" + "type": "AlkType:Uint16" }, "mapping": { - "5": {"TypeDef:Struct": true, "properties": {"id": {"TypeDef:Uint32": true}}} + "5": {"AlkType:Struct": true, "properties": {"id": {"AlkType:Uint32": true}}} } }); let mut buffer = vec![0u8; 16]; @@ -110,16 +110,16 @@ fn read_byte_discriminator_uint16_little_endian() -> Result<(), TypedefError> { } #[test] -fn read_byte_discriminator_uint32_big_endian() -> Result<(), TypedefError> { +fn read_byte_discriminator_uint32_big_endian() -> Result<(), AlkTypeError> { let schema = json!({ - "TypeDef:Union": true, + "AlkType:Union": true, "discriminator": { "kind": "byte", "offset": 0, - "type": "TypeDef:Uint32" + "type": "AlkType:Uint32" }, "mapping": { - "101": {"TypeDef:Struct": true, "properties": {"id": {"TypeDef:Uint32": true}}} + "101": {"AlkType:Struct": true, "properties": {"id": {"AlkType:Uint32": true}}} } }); let mut buffer = vec![0u8; 16]; @@ -132,21 +132,21 @@ fn read_byte_discriminator_uint32_big_endian() -> Result<(), TypedefError> { } #[test] -fn read_byte_discriminator_unknown_value_returns_access_error() -> Result<(), TypedefError> { +fn read_byte_discriminator_unknown_value_returns_access_error() -> Result<(), AlkTypeError> { let union_schema = sftp_like_byte_union(); let buffer = [99u8, 0x00, 0x00, 0x00]; let err = tunion::read_byte_discriminator(&buffer, &union_schema, Endian::Big).unwrap_err(); - assert!(matches!(err, TypedefError::Access { .. }), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Access { .. }), "got {err:?}"); Ok(()) } #[test] -fn read_field_discriminator_string_dispatches_to_read() -> Result<(), TypedefError> { +fn read_field_discriminator_string_dispatches_to_read() -> Result<(), AlkTypeError> { let union_schema = json!({ - "TypeDef:Union": true, + "AlkType:Union": true, "discriminator": {"kind": "field", "name": "type"}, "properties": { - "type": { "TypeDef:String": true } + "type": { "AlkType:String": true } }, "mapping": { "read": {"$ref": "#/$defs/Read"}, @@ -154,17 +154,17 @@ fn read_field_discriminator_string_dispatches_to_read() -> Result<(), TypedefErr }, "$defs": { "Read": { - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "handle": { "TypeDef:Uint32": true }, - "length": { "TypeDef:Uint32": true } + "handle": { "AlkType:Uint32": true }, + "length": { "AlkType:Uint32": true } } }, "Write": { - "TypeDef:Struct": true, + "AlkType:Struct": true, "properties": { - "handle": { "TypeDef:Uint32": true }, - "data": { "TypeDef:Bytes": true } + "handle": { "AlkType:Uint32": true }, + "data": { "AlkType:Bytes": true } } } } @@ -180,7 +180,7 @@ fn read_field_discriminator_string_dispatches_to_read() -> Result<(), TypedefErr let variant = tunion::resolve_variant(&union_schema, &dispatch.key)?; assert_eq!( variant - .get("TypeDef:Struct") + .get("AlkType:Struct") .and_then(serde_json::Value::as_bool), Some(true) ); @@ -188,12 +188,12 @@ fn read_field_discriminator_string_dispatches_to_read() -> Result<(), TypedefErr } #[test] -fn read_field_discriminator_string_dispatches_to_write() -> Result<(), TypedefError> { +fn read_field_discriminator_string_dispatches_to_write() -> Result<(), AlkTypeError> { let union_schema = json!({ - "TypeDef:Union": true, + "AlkType:Union": true, "discriminator": {"kind": "field", "name": "type"}, "properties": { - "type": { "TypeDef:String": true } + "type": { "AlkType:String": true } }, "mapping": { "read": {"$ref": "#/$defs/Read"}, @@ -201,12 +201,12 @@ fn read_field_discriminator_string_dispatches_to_write() -> Result<(), TypedefEr }, "$defs": { "Read": { - "TypeDef:Struct": true, - "properties": {"x": {"TypeDef:Uint8": true}} + "AlkType:Struct": true, + "properties": {"x": {"AlkType:Uint8": true}} }, "Write": { - "TypeDef:Struct": true, - "properties": {"y": {"TypeDef:Uint16": true}} + "AlkType:Struct": true, + "properties": {"y": {"AlkType:Uint16": true}} } } }); @@ -228,16 +228,16 @@ fn read_field_discriminator_string_dispatches_to_write() -> Result<(), TypedefEr } #[test] -fn read_field_discriminator_uint8_field() -> Result<(), TypedefError> { +fn read_field_discriminator_uint8_field() -> Result<(), AlkTypeError> { let union_schema = json!({ - "TypeDef:Union": true, + "AlkType:Union": true, "discriminator": {"kind": "field", "name": "tag"}, "properties": { - "tag": { "TypeDef:Uint8": true } + "tag": { "AlkType:Uint8": true } }, "mapping": { - "0": {"TypeDef:Struct": true, "properties": {"a": {"TypeDef:Uint32": true}}}, - "1": {"TypeDef:Struct": true, "properties": {"b": {"TypeDef:Uint16": true}}} + "0": {"AlkType:Struct": true, "properties": {"a": {"AlkType:Uint32": true}}}, + "1": {"AlkType:Struct": true, "properties": {"b": {"AlkType:Uint16": true}}} } }); let mut buffer = vec![0u8; 8]; @@ -254,40 +254,40 @@ fn read_field_discriminator_uint8_field() -> Result<(), TypedefError> { } #[test] -fn read_field_discriminator_unknown_value_returns_access_error() -> Result<(), TypedefError> { +fn read_field_discriminator_unknown_value_returns_access_error() -> Result<(), AlkTypeError> { let union_schema = json!({ - "TypeDef:Union": true, + "AlkType:Union": true, "discriminator": {"kind": "field", "name": "tag"}, "properties": { - "tag": { "TypeDef:Uint8": true } + "tag": { "AlkType:Uint8": true } }, "mapping": { - "0": {"TypeDef:Struct": true, "properties": {"a": {"TypeDef:Uint32": true}}} + "0": {"AlkType:Struct": true, "properties": {"a": {"AlkType:Uint32": true}}} } }); let mut buffer = vec![0u8; 8]; buffer[0] = 99; let err = tunion::read_field_discriminator(&buffer, &union_schema, 0, Endian::Little).unwrap_err(); - assert!(matches!(err, TypedefError::Access { .. }), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Access { .. }), "got {err:?}"); Ok(()) } #[test] -fn discriminator_size_returns_correct_values() -> Result<(), TypedefError> { +fn discriminator_size_returns_correct_values() -> Result<(), AlkTypeError> { let u8_schema = json!({ - "TypeDef:Union": true, - "discriminator": {"kind": "byte", "type": "TypeDef:Uint8"}, + "AlkType:Union": true, + "discriminator": {"kind": "byte", "type": "AlkType:Uint8"}, "mapping": {} }); let u16_schema = json!({ - "TypeDef:Union": true, - "discriminator": {"kind": "byte", "type": "TypeDef:Uint16"}, + "AlkType:Union": true, + "discriminator": {"kind": "byte", "type": "AlkType:Uint16"}, "mapping": {} }); let u32_schema = json!({ - "TypeDef:Union": true, - "discriminator": {"kind": "byte", "type": "TypeDef:Uint32"}, + "AlkType:Union": true, + "discriminator": {"kind": "byte", "type": "AlkType:Uint32"}, "mapping": {} }); assert_eq!(tunion::discriminator_size(&u8_schema)?, 1); @@ -299,25 +299,25 @@ fn discriminator_size_returns_correct_values() -> Result<(), TypedefError> { #[test] fn discriminator_size_field_kind_returns_schema_error() { let schema = json!({ - "TypeDef:Union": true, + "AlkType:Union": true, "discriminator": {"kind": "field", "name": "type"}, - "properties": {"type": {"TypeDef:Uint8": true}}, + "properties": {"type": {"AlkType:Uint8": true}}, "mapping": {} }); let err = tunion::discriminator_size(&schema).unwrap_err(); - assert!(matches!(err, TypedefError::Schema(_)), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Schema(_)), "got {err:?}"); } #[test] fn resolve_variant_returns_schema_error_for_unknown_key() { let union_schema = sftp_like_byte_union(); let err = tunion::resolve_variant(&union_schema, "999").unwrap_err(); - assert!(matches!(err, TypedefError::Schema(_)), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Schema(_)), "got {err:?}"); } #[test] fn parse_discriminator_missing_returns_schema_error() { - let schema = json!({"TypeDef:Union": true}); + let schema = json!({"AlkType:Union": true}); let err = alktype::parse_discriminator(&schema).unwrap_err(); - assert!(matches!(err, TypedefError::Schema(_)), "got {err:?}"); + assert!(matches!(err, AlkTypeError::Schema(_)), "got {err:?}"); }