Draft builder API (ADR-009) and generalized validation validate_bytes (ADR-010) for v0.1.0
- ADR-009: fluent Rust builder producing serde_json::Value, covers AlkType kinds + standard JSON Schema; resolves OQ-003 (alkcall is the unblocking consumer) - ADR-010: AlkTypeEngine::validate_bytes(&[u8]) as the single-call binary-buffer validation entry point; materialize Value from bytes, then validate; two methods on one struct, not a trait - builder.md: full builder API spec (Schema, Definitions, Discriminator) with four usage examples (channels chunk header, call input schema, SFTP Packet union, OperationSpec error schemas) - validation.md: new validate_bytes subsection + entry-point comparison table; AlkTypeEngine impl block updated; design decisions table updated - overview.md: builder.md added to component pointers; 'Not a schema builder' scope boundary retired; ADR-009/010 added to decisions table; OQ-003 marked resolved; Consumers table adds alkcall as first consumer - open-questions.md + questions/003: OQ-003 moved from deferred(scope) to resolved (ADR-009) - README.md: builder doc + ADR-009/010 added; OQ-003 marked resolved; two new Key Design Principles (9, 10) for v0.1.0 additions Doc-only change; 346 tests pass, clippy clean.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
---
|
||||
status: draft
|
||||
last_updated: 2026-07-22
|
||||
last_updated: 2026-08-11
|
||||
---
|
||||
|
||||
# alktype — Validation
|
||||
@@ -79,6 +79,9 @@ impl AlkTypeEngine {
|
||||
pub fn offset_map(&self) -> Option<&OffsetMap>; // Some in aligned mode
|
||||
pub fn layout_builder(&self) -> Option<&LayoutBuilder>; // Some in packed mode
|
||||
pub fn sequential_reader(&self) -> Option<SequentialReader>; // owned fresh reader (ADR-007)
|
||||
pub fn validate_json(&self, instance: &Value) -> Result<(), AlkTypeError>; // ADR-004
|
||||
pub fn is_valid_json(&self, instance: &Value) -> bool; // ADR-004
|
||||
pub fn validate_bytes(&self, buffer: &[u8]) -> Result<(), AlkTypeError>; // ADR-010
|
||||
}
|
||||
```
|
||||
|
||||
@@ -294,6 +297,75 @@ High-throughput paths can skip validation. Security-sensitive paths
|
||||
(parsing incoming frames from untrusted peers) can validate every frame.
|
||||
The choice is the consumer's.
|
||||
|
||||
### Access time: `engine.validate_bytes(&[u8])` — binary buffer validation
|
||||
|
||||
For binary-layout schemas (schemas declaring `AlkType:*` kinds), the
|
||||
engine offers a single-call form of the two-step dance: walk the bytes
|
||||
against the layout to materialize a `Value` tree, then validate that
|
||||
`Value` against the compiled jsonschema validator. Decided in
|
||||
[ADR-010](decisions/010-generalized-validation-validate-bytes.md).
|
||||
|
||||
```rust
|
||||
pub fn validate_bytes(&self, buffer: &[u8]) -> Result<(), AlkTypeError>;
|
||||
```
|
||||
|
||||
`validate_bytes` runs the existing machinery in sequence:
|
||||
|
||||
1. **Materialize `Value` from bytes.** A new internal helper
|
||||
(`materialize_value`, alongside `SequentialReader::read_field_value`
|
||||
in `src/sequential_reader.rs`) walks the buffer against the schema
|
||||
and the engine's `Endian`, producing a `serde_json::Value` tree.
|
||||
Composites are recursed into (`Struct` → object of field values;
|
||||
`Array` → array of element values; `Union` → dispatch then recurse;
|
||||
`Record` → object of key/value entries). The read phase reuses the
|
||||
existing data-access functions and returns `AlkTypeError::Access`
|
||||
(with field paths) on read failures.
|
||||
2. **Validate the `Value`.** The materialized `Value` is passed to the
|
||||
existing `self.validator.validate(&value)`, producing
|
||||
`AlkTypeError::Validation` on failure.
|
||||
|
||||
Mode dispatch:
|
||||
|
||||
- **Packed mode** — walks with a fresh `SequentialReader` (the engine
|
||||
is already a reader factory per ADR-007), materializing fields in
|
||||
declaration order.
|
||||
- **Aligned mode** — uses the `OffsetMap` to read fields at their
|
||||
computed offsets, then materializes composites by recursing into the
|
||||
offset map's nested entries.
|
||||
|
||||
Both modes produce the same `Value` form; the validator is
|
||||
mode-agnostic (it operates on `Value`, not bytes — ADR-004).
|
||||
|
||||
#### When to use which entry point
|
||||
|
||||
| Entry point | Schema form | Input form | When |
|
||||
|-------------|--------------|------------|------|
|
||||
| `validate_json(&Value)` | Any (AlkType or plain JSON Schema) | Already-parsed `serde_json::Value` | Call's JSON payloads (`OperationSpec.input_schema`); TypeBox output; anything off `serde_json::from_slice` / `from_str` |
|
||||
| `validate_bytes(&[u8])` | AlkType binary-layout schema | Raw `&[u8]` buffer | Channels' 8-byte chunk header; future binary call frames; SFTP packet buffers; metatensor index structs |
|
||||
|
||||
`validate_bytes` requires the engine's schema to declare `AlkType:*`
|
||||
kinds — it materializes `Value` via the layout engine, which needs
|
||||
binary-layout semantics. A pure JSON Schema (call's `input_schema`,
|
||||
no AlkType kinds) compiled via `AlkTypeEngine::compile` would fail at
|
||||
the materialize step (no `AlkType:Struct` at the root). For pure JSON
|
||||
payloads, the consumer uses `serde_json::from_slice` then
|
||||
`validate_json`. See [ADR-010](decisions/010-generalized-validation-validate-bytes.md)
|
||||
§"Not a binary-payload validator for JSON-only schemas".
|
||||
|
||||
#### What `validate_bytes` is not
|
||||
|
||||
- **Not a new validation engine.** It runs the existing `jsonschema`
|
||||
validator against the existing materialized `Value`. No new
|
||||
validator code, no parallel validation path (ADR-001).
|
||||
- **Not framing-aware.** It validates the bytes of *one* schema
|
||||
instance. It does not strip length prefixes, parse
|
||||
`[length: u32][payload]` framing, or handle multiple frames in a
|
||||
buffer. That's the consumer's job. alktype validates what one
|
||||
schema describes; it does not parse the wire envelope around it.
|
||||
- **Not a `Validator` trait.** Two methods on one struct, not a trait
|
||||
abstraction. See [ADR-010](decisions/010-generalized-validation-validate-bytes.md)
|
||||
§"Not a `Validator` trait abstraction".
|
||||
|
||||
## Relationship to Read/Write
|
||||
|
||||
Validation and data access are independent operations on the same data.
|
||||
@@ -315,13 +387,15 @@ representation first, then access the binary buffer.
|
||||
| Decision | ADR | Summary |
|
||||
|----------|-----|---------|
|
||||
| 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 |
|
||||
| Generalized validation — `validate_bytes` | [ADR-010](decisions/010-generalized-validation-validate-bytes.md) | Single-call binary-buffer validation (materialize `Value` from bytes, then validate); two methods on one struct, not a trait |
|
||||
| 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 alktype OQs (OQ-001, OQ-002,
|
||||
OQ-003) are about layout, platform support, and schema construction —
|
||||
not validation.
|
||||
not validation. OQ-003 is resolved by [ADR-009](decisions/009-builder-api.md);
|
||||
see [builder.md](builder.md).
|
||||
|
||||
## References
|
||||
|
||||
|
||||
Reference in New Issue
Block a user