Make BastDoc owned: drop Bast* lifetimes (ADR-012 §2a, plan phase 3)

Every Bast* type drops <'a>: &'a str -> String, &'a Value -> Value
(deferred decision 1: plain String/Value — the tree is built once;
Arc<str> name-sharing needs a bench justification that doesn't exist).
BastDoc::new(&Value, &str) still takes references in and clones into
owned storage; the doc gains Clone. resolve_ref/resolve_typeref/
resolve_typeref_as_def return owned types.

- Engine ownership flip: AlkTypeEngine holds the owned BastDoc
  (replacing bast_doc: Value + root_name: String; root_name()
  delegates to the doc), killing its three per-call BastDoc::new
  re-parses (aligned validate_bytes, read_field, write_field — the
  review #004 M1 pattern removed by construction; phase 5 retires the
  lookup_leaf_field walk itself). New public accessor root_name()
  (additive). Engine Send + Sync with the owned doc, asserted in the
  existing thread-share test.
- Bonus cleanup: materialize_typeref_packed's dead _field param
  dropped (phase 2 left it dangling). Under ownership, keeping it
  would force a deep Value clone per array element / record value /
  union variant via dummy_field_for. The param, dummy_field_for, and
  ty_source are gone; no behavior change (the arg was already
  ignored). BastField::synthetic keeps an owned-signature
  #[allow(dead_code)] definition (no remaining callers today).
- Consumers adapted: OffsetMap::compute(&BastDoc),
  materialize_aligned(&BastDoc, ...) (no lifetime), BuildCtx/
  ComputeCtx hold &'d BastDoc, tunion/discriminator name borrows,
  lib.rs module doc. LayoutBuilder's doc_value re-parse cache is
  unchanged pending phase 4.

Verification: 465 tests pass with zero test-logic changes (bast.rs
suites exercise every parser path through the public API); clippy
-D warnings clean; cargo doc zero warnings; wasm32 release build
green.
This commit is contained in:
glm-5.3-flash committed 2026-09-02 08:31:58 +00:00
1 parent 1641dab505
commit c583762352
12 files changed
+299 -295

No files matched your search

+29 -1
View File
@@ -379,7 +379,35 @@ message).
---
## Phase 3 — Owned `BastDoc` (ADR-012 §2a)
## Phase 3 — Owned `BastDoc` (ADR-012 §2a) — **DONE (2026-09-02)**
> **Status: implemented.** Every `Bast*` type dropped its `<'a>`:
> `&'a str` → `String`, `&'a Value` → `Value` (deferred decision 1
> resolved: plain `String`/`Value` — the tree is built once, name
> sharing via `Arc<str>` needs a bench justification that doesn't
> exist). `BastDoc::new(&Value, &str)` still takes references in and
> clones into owned storage; `BastDoc` gained `Clone`. `resolve_*`
> return owned `BastDef`/`BastType`. Consumers adapted:
> `OffsetMap::compute(&BastDoc)`, `materialize_aligned(&BastDoc, ...)`
> (no lifetime), `BuildCtx`/`ComputeCtx` hold `&'d BastDoc`.
> **Engine ownership flip:** `AlkTypeEngine` now holds the owned
> `BastDoc` (replacing `bast_doc: Value` + `root_name: String` —
> `root_name()` delegates to the doc), killing its three per-call
> `BastDoc::new` re-parses (`validate_bytes` aligned path,
> `read_field`, `write_field` — review #004 M1 sites by construction;
> phase 5 removes the `lookup_leaf_field` walk itself). New public
> accessor `AlkTypeEngine::root_name()` (additive). **Bonus cleanup:**
> `materialize_typeref_packed`'s dead `_field` param dropped (phase 2
> left it dangling); under ownership it would have forced a deep
> `Value` clone per array element/record value/union variant via
> `dummy_field_for` — the param, `dummy_field_for`, and `ty_source`
> are gone (no behavior change; the `_field` arg was already
> ignored). `BastField::synthetic` retains an owned-signature
> `#[allow(dead_code)]` definition (no remaining callers; kept for the
> phase-4/5-aligned materializer helpers if they need it). Existing
> `BastDoc` consumers (`LayoutBuilder`'s `doc_value` re-parse cache)
> are unchanged pending phase 4. Engine stays `Send + Sync` with the
> owned doc — the engine's thread-share test now asserts it directly.
**Goal:** Make `BastDoc` own its data (drop the `<'a>` lifetime).
`&'a str` → `String`, `&'a Value` → `Value` (or `Arc<str>`/`Arc<Value>`
+155 -145
View File
@@ -11,11 +11,13 @@
//! ## Design
//!
//! The parser produces a **typed tree** ([`BastDoc`]/[`BastDef`]/[`BastType`]/…)
//! that borrows from the source [`serde_json::Value`]. Three consumers
//! (layout, materialize, validate) walk the same tree, so a typed view
//! pays for itself: each walks matched arms over [`BastType`] instead of
//! re-parsing raw JSON at every node. Borrowing (not cloning) keeps the
//! parse allocation-free beyond the small typed nodes themselves.
//! that owns its data (ADR-012 §2a). Three consumers (layout, materialize,
//! validate) walk the same tree, so a typed view pays for itself: each
//! walks matched arms over [`BastType`] instead of re-parsing raw JSON at
//! every node. Ownership (not borrowing) lets [`BastDoc`] be stored on
//! its consumers (`AlkTypeEngine`, `LayoutBuilder`) without lifetime
//! entanglement — the clone happens once at [`BastDoc::new`], not per
//! field.
//!
//! `$ref` resolution is a single hash lookup against `$defs` — BAST refs
//! are always full JSON Pointers restricted to `#/$defs/<name>`. Union
@@ -45,16 +47,18 @@ const REF_PREFIX: &str = "#/$defs/";
///
/// Built once via [`BastDoc::new`] from the raw BAST `Value`; the
/// downstream `compile()` call (step 4) will construct this and hand it
/// to the layout engines. The tree borrows from `root` — keep the
/// source `Value` alive for the lifetime of the doc.
#[derive(Debug)]
pub struct BastDoc<'a> {
root: &'a Value,
root_name: &'a str,
root_def: BastDef<'a>,
/// to the layout engines. The tree is **owned** (ADR-012 §2a): `new`
/// clones the root `Value` and the root name into the doc, so the doc
/// can be stored on consumers without borrowing from the caller's
/// input.
#[derive(Debug, Clone)]
pub struct BastDoc {
root: Value,
root_name: String,
root_def: BastDef,
}
impl<'a> BastDoc<'a> {
impl BastDoc {
/// Parse a BAST document, selecting `root_name` as the top-level type.
///
/// Validates the document structurally as it parses: the `$defs`
@@ -64,26 +68,27 @@ impl<'a> BastDoc<'a> {
/// root definition and the definitions it (transitively) references
/// are parsed eagerly; orphan `$defs` entries are not checked.
///
/// The raw `Value` is also retained so lazy `$ref` resolution
/// ([`BastDoc::resolve_typeref`]) can reach any `$defs` entry at access time.
pub fn new(root: &'a Value, root_name: &'a str) -> Result<Self, AlkTypeError> {
/// The raw `Value` is cloned into the doc so lazy `$ref` resolution
/// ([`BastDoc::resolve_typeref`]) can reach any `$defs` entry at
/// access time.
pub fn new(root: &Value, root_name: &str) -> Result<Self, AlkTypeError> {
let raw_root_def = Self::lookup_def_raw(root, root_name)?;
let root_def = BastDef::parse(raw_root_def, root_name, "")?;
let doc = Self {
root,
root_name,
root: root.clone(),
root_name: root_name.to_string(),
root_def,
};
Ok(doc)
}
/// The root type name this doc was compiled with.
pub fn root_name(&self) -> &'a str {
self.root_name
pub fn root_name(&self) -> &str {
&self.root_name
}
/// The parsed root type definition.
pub fn root_def(&self) -> &BastDef<'a> {
pub fn root_def(&self) -> &BastDef {
&self.root_def
}
@@ -92,8 +97,8 @@ impl<'a> BastDoc<'a> {
/// The single hash lookup that replaces v0.1.0's ref-normalization
/// machinery. BAST refs are always `#/$defs/<name>`;
/// no external refs, no fragment-only pointers, no bare names.
pub fn lookup_def(&self, name: &str) -> Result<&'a Value, AlkTypeError> {
Self::lookup_def_raw(self.root, name)
pub fn lookup_def(&self, name: &str) -> Result<&Value, AlkTypeError> {
Self::lookup_def_raw(&self.root, name)
}
/// Resolve a [`BastType::Ref`] to the [`BastDef`] it names.
@@ -102,7 +107,7 @@ impl<'a> BastDoc<'a> {
/// for union variant dispatch and `$ref` fields. The parser does not
/// inline refs at construction time — variant refs stay as
/// [`BastRef`]s and resolve on demand.
pub fn resolve_ref(&self, r: &BastRef<'a>) -> Result<BastDef<'a>, AlkTypeError> {
pub fn resolve_ref(&self, r: &BastRef) -> Result<BastDef, AlkTypeError> {
let raw = self.lookup_def(r.name())?;
BastDef::parse(raw, r.name(), "")
}
@@ -114,7 +119,7 @@ impl<'a> BastDoc<'a> {
/// The composite-walkers (struct fields, array elements, record
/// values, union mappings) call this to deref one level. Deeper
/// `$ref` chains are resolved by recursing through the consumer.
pub fn resolve_typeref(&self, ty: &BastType<'a>) -> Result<BastType<'a>, AlkTypeError> {
pub fn resolve_typeref(&self, ty: &BastType) -> Result<BastType, AlkTypeError> {
match ty {
BastType::Ref(r) => {
let def = self.resolve_ref(r)?;
@@ -134,25 +139,25 @@ impl<'a> BastDoc<'a> {
/// uniformly.
pub fn resolve_typeref_as_def(
&self,
ty: &BastType<'a>,
ty: &BastType,
owning_path: &str,
) -> Result<BastDef<'a>, AlkTypeError> {
) -> Result<BastDef, AlkTypeError> {
match ty {
BastType::Ref(r) => self.resolve_ref(r),
BastType::Struct(s) => Ok(BastDef {
name: "",
name: String::new(),
kind: BastDefKind::Struct(s.clone()),
source: s.source,
source: s.source.clone(),
}),
BastType::Union(u) => Ok(BastDef {
name: "",
name: String::new(),
kind: BastDefKind::Union(u.clone()),
source: u.source,
source: u.source.clone(),
}),
BastType::Enum(e) => Ok(BastDef {
name: "",
name: String::new(),
kind: BastDefKind::Enum(e.clone()),
source: e.source,
source: e.source.clone(),
}),
BastType::Primitive(_) | BastType::Array(_) | BastType::Record(_) => {
Err(AlkTypeError::Schema(format!(
@@ -162,7 +167,7 @@ impl<'a> BastDoc<'a> {
}
}
fn lookup_def_raw(root: &'a Value, name: &str) -> Result<&'a Value, AlkTypeError> {
fn lookup_def_raw<'d>(root: &'d Value, name: &str) -> Result<&'d Value, AlkTypeError> {
let defs = root
.get(DEFS_KEY)
.and_then(Value::as_object)
@@ -180,33 +185,33 @@ impl<'a> BastDoc<'a> {
/// arrays, and records only appear as field/element/value types
/// ([`BastType`]), not as named definitions.
#[derive(Debug, Clone)]
pub struct BastDef<'a> {
name: &'a str,
kind: BastDefKind<'a>,
source: &'a Value,
pub struct BastDef {
name: String,
kind: BastDefKind,
source: Value,
}
impl<'a> BastDef<'a> {
impl BastDef {
/// The `$defs` key this definition was registered under. Empty
/// string for synthetic definitions produced by
/// [`BastDoc::resolve_typeref_as_def`] from an inline type.
pub fn name(&self) -> &'a str {
self.name
pub fn name(&self) -> &str {
&self.name
}
/// The typed kind — struct, union, or enum.
pub fn kind(&self) -> &BastDefKind<'a> {
pub fn kind(&self) -> &BastDefKind {
&self.kind
}
/// The raw JSON node this definition was parsed from. Retained for
/// consumers that need properties the typed view doesn't expose yet
/// (e.g. the layout engines during the step 4 migration).
pub fn source(&self) -> &'a Value {
self.source
pub fn source(&self) -> &Value {
&self.source
}
fn parse(node: &'a Value, name: &'a str, path: &str) -> Result<Self, AlkTypeError> {
fn parse(node: &Value, name: &str, path: &str) -> Result<Self, AlkTypeError> {
let kind_str = node
.get("kind")
.and_then(Value::as_str)
@@ -233,25 +238,25 @@ impl<'a> BastDef<'a> {
}
};
Ok(Self {
name,
name: name.to_string(),
kind,
source: node,
source: node.clone(),
})
}
}
/// The typed kind of a [`BastDef`].
#[derive(Debug, Clone)]
pub enum BastDefKind<'a> {
pub enum BastDefKind {
/// `kind: "struct"` — an ordered list of fields.
Struct(BastStruct<'a>),
Struct(BastStruct),
/// `kind: "union"` — discriminator + mapping of variant TypeRefs.
Union(BastUnion<'a>),
Union(BastUnion),
/// `kind: "enum"` — a non-empty list of string values.
Enum(BastEnum<'a>),
Enum(BastEnum),
}
impl<'a> BastDefKind<'a> {
impl BastDefKind {
/// The [`AlkTypeKind`] of this definition.
pub fn alk_kind(&self) -> AlkTypeKind {
match self {
@@ -269,14 +274,14 @@ impl<'a> BastDefKind<'a> {
/// ([bast-format.md §Design Principles](../docs/architecture/bast-format.md#design-principles)
/// #4).
#[derive(Debug, Clone)]
pub struct BastStruct<'a> {
pub struct BastStruct {
endian: Endian,
align: Option<usize>,
fields: Vec<BastField<'a>>,
source: &'a Value,
fields: Vec<BastField>,
source: Value,
}
impl<'a> BastStruct<'a> {
impl BastStruct {
/// The struct-level default endianness. Defaults to
/// [`Endian::Little`] when absent (ADR-003).
pub fn endian(&self) -> Endian {
@@ -290,16 +295,16 @@ impl<'a> BastStruct<'a> {
}
/// The ordered field list. Array position is field order.
pub fn fields(&self) -> &[BastField<'a>] {
pub fn fields(&self) -> &[BastField] {
&self.fields
}
/// The raw JSON node this struct was parsed from.
pub fn source(&self) -> &'a Value {
self.source
pub fn source(&self) -> &Value {
&self.source
}
fn parse(node: &'a Value, path: &str) -> Result<Self, AlkTypeError> {
fn parse(node: &Value, path: &str) -> Result<Self, AlkTypeError> {
let endian = parse_endian_opt(node).unwrap_or(Endian::Little);
let align = parse_align(node);
let raw_fields = node
@@ -319,32 +324,33 @@ impl<'a> BastStruct<'a> {
endian,
align,
fields,
source: node,
source: node.clone(),
})
}
}
/// A field within a struct or field-name-discriminator union.
#[derive(Debug, Clone)]
pub struct BastField<'a> {
name: &'a str,
ty: BastType<'a>,
pub struct BastField {
name: String,
ty: BastType,
endian: Option<Endian>,
align: Option<usize>,
encoding: VariableEncoding,
max_length: Option<usize>,
source: &'a Value,
source: Value,
}
impl<'a> BastField<'a> {
impl BastField {
/// Construct a synthetic field wrapping `ty` with no annotations.
/// Used internally by the materializer for array elements, record
/// values, and union variants — these are TypeRefs, not fields, so
/// they don't carry field-level annotations. `source` is the
/// `BastType`'s source node (or `&Value::Null` for primitives).
pub(crate) fn synthetic(ty: BastType<'a>, source: &'a Value) -> Self {
#[allow(dead_code)]
pub(crate) fn synthetic(ty: BastType, source: Value) -> Self {
Self {
name: "",
name: String::new(),
ty,
endian: None,
align: None,
@@ -356,12 +362,12 @@ impl<'a> BastField<'a> {
/// The field name. Guaranteed to match `^[a-zA-Z_][a-zA-Z0-9_]*$` by
/// the meta-schema; the parser does not re-check the pattern.
pub fn name(&self) -> &'a str {
self.name
pub fn name(&self) -> &str {
&self.name
}
/// The field's type — a primitive, `$ref`, array, or record.
pub fn ty(&self) -> &BastType<'a> {
pub fn ty(&self) -> &BastType {
&self.ty
}
@@ -391,8 +397,8 @@ impl<'a> BastField<'a> {
}
/// The raw JSON node this field was parsed from.
pub fn source(&self) -> &'a Value {
self.source
pub fn source(&self) -> &Value {
&self.source
}
/// The effective endianness: this field's override, or the container
@@ -401,7 +407,7 @@ impl<'a> BastField<'a> {
self.endian.unwrap_or(default)
}
fn parse(node: &'a Value, path: &str) -> Result<Self, AlkTypeError> {
fn parse(node: &Value, path: &str) -> Result<Self, AlkTypeError> {
let name = node
.get("name")
.and_then(Value::as_str)
@@ -423,13 +429,13 @@ impl<'a> BastField<'a> {
let encoding = parse_encoding(node);
let max_length = parse_max_length(node);
Ok(Self {
name,
name: name.to_string(),
ty,
endian,
align,
encoding,
max_length,
source: node,
source: node.clone(),
})
}
}
@@ -440,52 +446,52 @@ impl<'a> BastField<'a> {
/// Variant `$ref`s are resolved **lazily** by the materializer/validator
/// via [`BastDoc::resolve_typeref`] — no compile-time inlining.
#[derive(Debug, Clone)]
pub struct BastUnion<'a> {
pub struct BastUnion {
endian: Endian,
discriminator: BastDiscriminator<'a>,
fields: Vec<BastField<'a>>,
mapping: Vec<(&'a str, BastType<'a>)>,
source: &'a Value,
discriminator: BastDiscriminator,
fields: Vec<BastField>,
mapping: Vec<(String, BastType)>,
source: Value,
}
impl<'a> BastUnion<'a> {
impl BastUnion {
/// The union-level default endianness for variant fields.
pub fn endian(&self) -> Endian {
self.endian
}
/// The discriminator — byte-offset or field-name.
pub fn discriminator(&self) -> &BastDiscriminator<'a> {
pub fn discriminator(&self) -> &BastDiscriminator {
&self.discriminator
}
/// The optional `fields` array. Only valid with field-name
/// discriminators (D-BAST-005); empty for byte-offset discriminators.
pub fn fields(&self) -> &[BastField<'a>] {
pub fn fields(&self) -> &[BastField] {
&self.fields
}
/// The `mapping` entries, in document order. Each key is a
/// stringified discriminator value; each value is the variant
/// [`BastType`] (typically a `$ref`).
pub fn mapping(&self) -> &[(&'a str, BastType<'a>)] {
pub fn mapping(&self) -> &[(String, BastType)] {
&self.mapping
}
/// Look up the variant [`BastType`] for a discriminator value.
pub fn variant_for(&self, key: &str) -> Option<&BastType<'a>> {
pub fn variant_for(&self, key: &str) -> Option<&BastType> {
self.mapping
.iter()
.find(|(k, _)| *k == key)
.find(|(k, _)| k == key)
.map(|(_, v)| v)
}
/// The raw JSON node this union was parsed from.
pub fn source(&self) -> &'a Value {
self.source
pub fn source(&self) -> &Value {
&self.source
}
fn parse(node: &'a Value, path: &str) -> Result<Self, AlkTypeError> {
fn parse(node: &Value, path: &str) -> Result<Self, AlkTypeError> {
let endian = parse_endian_opt(node).unwrap_or(Endian::Little);
let discriminator = BastDiscriminator::parse(node, path)?;
let raw_fields = node.get("fields").and_then(Value::as_array);
@@ -524,7 +530,7 @@ impl<'a> BastUnion<'a> {
for (key, value) in raw_mapping.iter() {
let entry_path = format!("{path}.mapping[{key}]");
let ty = BastType::parse(value, &entry_path)?;
mapping.push((key.as_str(), ty));
mapping.push((key.clone(), ty));
}
if mapping.is_empty() {
return Err(AlkTypeError::Schema(format!(
@@ -536,14 +542,14 @@ impl<'a> BastUnion<'a> {
discriminator,
fields,
mapping,
source: node,
source: node.clone(),
})
}
}
/// A union discriminator — byte-offset or field-name (ADR-003, D-BAST-005).
#[derive(Debug, Clone)]
pub enum BastDiscriminator<'a> {
pub enum BastDiscriminator {
/// A fixed-size integer at a known byte offset. Mapping keys are
/// stringified integers.
Byte {
@@ -557,12 +563,12 @@ pub enum BastDiscriminator<'a> {
/// declares the discriminator field alongside any shared fields.
Field {
/// The field name that holds the discriminator value.
name: &'a str,
name: String,
},
}
impl<'a> BastDiscriminator<'a> {
fn parse(union_node: &'a Value, path: &str) -> Result<Self, AlkTypeError> {
impl BastDiscriminator {
fn parse(union_node: &Value, path: &str) -> Result<Self, AlkTypeError> {
let disc = union_node.get("discriminator").ok_or_else(|| {
AlkTypeError::Schema(format!(
"bast: union at {path} has no `discriminator`"
@@ -613,7 +619,9 @@ impl<'a> BastDiscriminator<'a> {
"bast: field discriminator at {path} has no `name`"
))
})?;
Ok(BastDiscriminator::Field { name })
Ok(BastDiscriminator::Field {
name: name.to_string(),
})
}
other => Err(AlkTypeError::Schema(format!(
"bast: unknown discriminator kind {other:?} at {path}"
@@ -631,24 +639,24 @@ impl<'a> BastDiscriminator<'a> {
/// built-in `enum` keyword checked string membership but the materializer
/// emitted a numeric index that never matched.
#[derive(Debug, Clone)]
pub struct BastEnum<'a> {
values: Vec<&'a str>,
source: &'a Value,
pub struct BastEnum {
values: Vec<String>,
source: Value,
}
impl<'a> BastEnum<'a> {
impl BastEnum {
/// The declared values, in declaration order. Non-empty (enforced
/// by the meta-schema's `minItems: 1` and re-checked here).
pub fn values(&self) -> &[&'a str] {
pub fn values(&self) -> &[String] {
&self.values
}
/// The raw JSON node this enum was parsed from.
pub fn source(&self) -> &'a Value {
self.source
pub fn source(&self) -> &Value {
&self.source
}
fn parse(node: &'a Value, path: &str) -> Result<Self, AlkTypeError> {
fn parse(node: &Value, path: &str) -> Result<Self, AlkTypeError> {
let raw_values = node
.get("values")
.and_then(Value::as_array)
@@ -669,11 +677,11 @@ impl<'a> BastEnum<'a> {
"bast: enum at {path} values[{i}] is not a string"
))
})?;
values.push(s);
values.push(s.to_string());
}
Ok(Self {
values,
source: node,
source: node.clone(),
})
}
}
@@ -688,25 +696,25 @@ impl<'a> BastEnum<'a> {
/// `enum`) appear inline here when a field/element/value is an anonymous
/// composite; named composites go through [`BastType::Ref`].
#[derive(Debug, Clone)]
pub enum BastType<'a> {
pub enum BastType {
/// A built-in primitive (`"uint32"`, `"string"`, etc.).
Primitive(AlkTypeKind),
/// A `$ref` to a named `$defs` entry: `{ "$ref": "#/$defs/Name" }`.
Ref(BastRef<'a>),
Ref(BastRef),
/// `kind: "array"` — a fixed-size array (count required in v1,
/// D-BAST-004).
Array(BastArray<'a>),
Array(BastArray),
/// `kind: "record"` — a string-keyed map.
Record(BastRecord<'a>),
Record(BastRecord),
/// An inline `kind: "struct"`.
Struct(BastStruct<'a>),
Struct(BastStruct),
/// An inline `kind: "union"`.
Union(BastUnion<'a>),
Union(BastUnion),
/// An inline `kind: "enum"`.
Enum(BastEnum<'a>),
Enum(BastEnum),
}
impl<'a> BastType<'a> {
impl BastType {
/// The [`AlkTypeKind`] of this type. For [`Ref`](Self::Ref), this is
/// [`AlkTypeKind::Struct`] — the meta-schema only allows
/// struct/union/enum as named definitions, so a `$ref` always lands
@@ -724,7 +732,7 @@ impl<'a> BastType<'a> {
}
}
fn parse(node: &'a Value, path: &str) -> Result<Self, AlkTypeError> {
fn parse(node: &Value, path: &str) -> Result<Self, AlkTypeError> {
if let Some(s) = node.as_str() {
let k = AlkTypeKind::from_bast_str(s)?;
return Ok(BastType::Primitive(k));
@@ -759,7 +767,7 @@ impl<'a> BastType<'a> {
}
}
fn from_def(def: BastDef<'a>) -> Self {
fn from_def(def: BastDef) -> Self {
match def.kind {
BastDefKind::Struct(s) => BastType::Struct(s),
BastDefKind::Union(u) => BastType::Union(u),
@@ -774,18 +782,18 @@ impl<'a> BastType<'a> {
/// ([`BastDoc::lookup_def`]); the v0.1.0 engine needed a normalization
/// pass for TypeBox's bare-name refs, but BAST forbids them.
#[derive(Debug, Clone)]
pub struct BastRef<'a> {
name: &'a str,
pub struct BastRef {
name: String,
}
impl<'a> BastRef<'a> {
impl BastRef {
/// The referenced `$defs` entry name (e.g. `Read` for
/// `#/$defs/Read`).
pub fn name(&self) -> &'a str {
self.name
pub fn name(&self) -> &str {
&self.name
}
fn parse(ref_path: &'a str, path: &str) -> Result<Self, AlkTypeError> {
fn parse(ref_path: &str, path: &str) -> Result<Self, AlkTypeError> {
let name = ref_path.strip_prefix(REF_PREFIX).ok_or_else(|| {
AlkTypeError::Schema(format!(
"bast: $ref at {path} is {ref_path:?}, expected \"#/$defs/<name>\""
@@ -796,7 +804,9 @@ impl<'a> BastRef<'a> {
"bast: $ref at {path} has an empty name after \"#/$defs/\""
)));
}
Ok(Self { name })
Ok(Self {
name: name.to_string(),
})
}
}
@@ -807,15 +817,15 @@ impl<'a> BastRef<'a> {
/// element type is itself a [`BastType`], so nested `$ref`s work:
/// `{ "kind": "array", "element": { "$ref": "#/$defs/Point" }, "count": 4 }`.
#[derive(Debug, Clone)]
pub struct BastArray<'a> {
element: Box<BastType<'a>>,
pub struct BastArray {
element: Box<BastType>,
count: usize,
source: &'a Value,
source: Value,
}
impl<'a> BastArray<'a> {
impl BastArray {
/// The element type.
pub fn element(&self) -> &BastType<'a> {
pub fn element(&self) -> &BastType {
&self.element
}
@@ -825,11 +835,11 @@ impl<'a> BastArray<'a> {
}
/// The raw JSON node this array type was parsed from.
pub fn source(&self) -> &'a Value {
self.source
pub fn source(&self) -> &Value {
&self.source
}
fn parse(node: &'a Value, path: &str) -> Result<Self, AlkTypeError> {
fn parse(node: &Value, path: &str) -> Result<Self, AlkTypeError> {
let element_node = node.get("element").ok_or_else(|| {
AlkTypeError::Schema(format!(
"bast: array at {path} has no `element`"
@@ -844,7 +854,7 @@ impl<'a> BastArray<'a> {
Ok(Self {
element: Box::new(element),
count,
source: node,
source: node.clone(),
})
}
}
@@ -855,23 +865,23 @@ impl<'a> BastArray<'a> {
/// `[count: u32][key_len: u32][key_bytes][value]...` repeated `count`
/// times (per [bast-format.md §Record](../docs/architecture/bast-format.md#record)).
#[derive(Debug, Clone)]
pub struct BastRecord<'a> {
values: Box<BastType<'a>>,
source: &'a Value,
pub struct BastRecord {
values: Box<BastType>,
source: Value,
}
impl<'a> BastRecord<'a> {
impl BastRecord {
/// The value type.
pub fn values(&self) -> &BastType<'a> {
pub fn values(&self) -> &BastType {
&self.values
}
/// The raw JSON node this record type was parsed from.
pub fn source(&self) -> &'a Value {
self.source
pub fn source(&self) -> &Value {
&self.source
}
fn parse(node: &'a Value, path: &str) -> Result<Self, AlkTypeError> {
fn parse(node: &Value, path: &str) -> Result<Self, AlkTypeError> {
let values_node = node.get("values").ok_or_else(|| {
AlkTypeError::Schema(format!(
"bast: record at {path} has no `values`"
@@ -880,7 +890,7 @@ impl<'a> BastRecord<'a> {
let values = BastType::parse(values_node, &format!("{path}.values"))?;
Ok(Self {
values: Box::new(values),
source: node,
source: node.clone(),
})
}
}
@@ -954,7 +964,7 @@ fn parse_usize_field(
}
}
impl<'a> std::fmt::Display for BastType<'a> {
impl std::fmt::Display for BastType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
BastType::Primitive(k) => write!(f, "{}", k.to_bast_str()),
@@ -973,7 +983,7 @@ mod tests {
use super::*;
use serde_json::json;
fn doc_from<'a>(root: &'a Value, root_name: &'a str) -> BastDoc<'a> {
fn doc_from(root: &Value, root_name: &str) -> BastDoc {
BastDoc::new(root, root_name).expect("bast doc")
}
@@ -1719,7 +1729,7 @@ mod tests {
let arr = BastType::Array(BastArray {
element: Box::new(BastType::Primitive(AlkTypeKind::Float32)),
count: 3,
source: &Value::Null,
source: Value::Null,
});
assert_eq!(format!("{arr}"), "array<float32>[3]");
}
+3 -3
View File
@@ -61,7 +61,7 @@ pub(crate) const DISCRIMINATOR_KEY: &str = "__discriminator";
/// constraint, or `Err(AlkTypeError::Schema(...))` if the BAST document
/// is malformed (a dangling `$ref`, a cyclic `$ref`, a missing `values`
/// array, etc.).
pub fn validate_value(doc: &BastDoc<'_>, value: &Value) -> Result<(), AlkTypeError> {
pub fn validate_value(doc: &BastDoc, value: &Value) -> Result<(), AlkTypeError> {
let plan = crate::validation_plan::ValidationPlan::compile(doc)?;
plan.validate(value)
}
@@ -85,7 +85,7 @@ mod tests {
use crate::read_plan::ReadPlan;
use serde_json::json;
fn doc_from<'a>(root: &'a Value, name: &'a str) -> BastDoc<'a> {
fn doc_from(root: &Value, name: &str) -> BastDoc {
BastDoc::new(root, name).expect("bast doc")
}
@@ -101,7 +101,7 @@ mod tests {
fn materialize_and_validate(
root: &Value,
doc: &BastDoc<'_>,
doc: &BastDoc,
buffer: &[u8],
) -> Result<(), AlkTypeError> {
let plan = ReadPlan::compile(root, doc.root_name())?;
+22 -17
View File
@@ -87,8 +87,7 @@ pub struct AlkTypeEngine {
json_validator: Option<jsonschema::Validator>,
validation_plan: Arc<ValidationPlan>,
endian: Endian,
bast_doc: Value,
root_name: String,
doc: BastDoc,
}
impl AlkTypeEngine {
@@ -114,10 +113,10 @@ impl AlkTypeEngine {
/// BAST document — BAST describes bytes, not JSON shape. It may be
/// authored separately or derived from BAST via future codegen.
///
/// The engine retains a clone of the BAST `Value` so that
/// [`AlkTypeEngine::sequential_reader`] and
/// [`AlkTypeEngine::read_field`] can re-parse the typed tree on
/// demand without lifetime entanglement with the caller's `Value`.
/// The engine retains an owned [`BastDoc`] (ADR-012 §2a) so that
/// [`AlkTypeEngine::validate_bytes`] and
/// [`AlkTypeEngine::read_field`] can walk the typed tree without
/// re-parsing the raw document per call.
///
/// # Errors
///
@@ -171,8 +170,7 @@ impl AlkTypeEngine {
json_validator,
validation_plan,
endian,
bast_doc: bast_doc.clone(),
root_name: root_name.to_string(),
doc,
})
}
@@ -181,6 +179,11 @@ impl AlkTypeEngine {
self.endian
}
/// The root type name this engine was compiled with (D-BAST-001).
pub fn root_name(&self) -> &str {
self.doc.root_name()
}
/// The layout mode this engine was compiled with.
pub fn mode(&self) -> LayoutMode {
match self.layout {
@@ -302,13 +305,12 @@ impl AlkTypeEngine {
/// (D-BAST-009 — the variant type stays uniform with the
/// `validate_json` path).
pub fn validate_bytes(&self, buffer: &[u8]) -> Result<(), AlkTypeError> {
let doc = BastDoc::new(&self.bast_doc, &self.root_name)?;
let value = match &self.layout {
Layout::Packed { plan, .. } => {
materialize::materialize_packed(plan, buffer)?
}
Layout::Aligned { offset_map } => {
materialize::materialize_aligned(&doc, buffer, offset_map)?
materialize::materialize_aligned(&self.doc, buffer, offset_map)?
}
};
self.validation_plan.validate(&value)
@@ -369,8 +371,7 @@ impl AlkTypeEngine {
field_path: field_path.to_string(),
reason: "field not found in offset map".to_string(),
})?;
let doc = BastDoc::new(&self.bast_doc, &self.root_name)?;
let leaf = lookup_leaf_field(&doc, field_path).ok_or_else(|| AlkTypeError::Offset {
let leaf = lookup_leaf_field(&self.doc, field_path).ok_or_else(|| AlkTypeError::Offset {
field_path: field_path.to_string(),
reason: "field schema not found in BAST tree or has no primitive kind".to_string(),
})?;
@@ -502,8 +503,7 @@ impl AlkTypeEngine {
field_path: field_path.to_string(),
reason: "field not found in offset map".to_string(),
})?;
let doc = BastDoc::new(&self.bast_doc, &self.root_name)?;
let leaf = lookup_leaf_field(&doc, field_path).ok_or_else(|| AlkTypeError::Offset {
let leaf = lookup_leaf_field(&self.doc, field_path).ok_or_else(|| AlkTypeError::Offset {
field_path: field_path.to_string(),
reason: "field schema not found in BAST tree or has no primitive kind".to_string(),
})?;
@@ -583,7 +583,7 @@ impl fmt::Debug for AlkTypeEngine {
.field("layout", &self.layout)
.field("json_validator", &self.json_validator.as_ref().map(|_| "<jsonschema::Validator>"))
.field("endian", &self.endian)
.field("root_name", &self.root_name)
.field("root_name", &self.root_name())
.finish()
}
}
@@ -605,9 +605,9 @@ struct LeafFieldInfo {
/// `fields` at each step, resolving `$ref`s via [`BastDoc::resolve_typeref`].
/// Array element segments (`field[i]`) are not handled here —
/// `read_field`/`write_field` only address leaf fields.
fn lookup_leaf_field(doc: &BastDoc<'_>, field_path: &str) -> Option<LeafFieldInfo> {
fn lookup_leaf_field(doc: &BastDoc, field_path: &str) -> Option<LeafFieldInfo> {
let root_def = doc.root_def();
let mut current_struct: BastStruct<'_> = match root_def.kind() {
let mut current_struct: BastStruct = match root_def.kind() {
BastDefKind::Struct(s) => s.clone(),
_ => return None,
};
@@ -1399,6 +1399,11 @@ mod tests {
AlkTypeEngine::compile(&doc, "S", LayoutMode::Packed, None).expect("compile");
fn assert_send_sync<T: Send + Sync>(_: &T) {}
assert_send_sync(engine.validation_plan());
// The engine itself must stay Send + Sync now that it holds the
// owned BastDoc (ADR-012 §2a) — alkcall shares engines across
// hub/spoke threads.
fn assert_engine_send_sync<T: Send + Sync>(_: &T) {}
assert_engine_send_sync(&engine);
let shared:std::sync::Arc<_> = engine.validation_plan().clone();
let handle = std::thread::spawn(move || shared.validate(&json!({"id": 1})).is_ok());
assert!(handle.join().expect("join"));
+7 -7
View File
@@ -214,7 +214,7 @@ impl LayoutBuilder {
/// Mutable context threaded through the recursive layout computation.
struct BuildCtx<'d> {
doc: &'d BastDoc<'d>,
doc: &'d BastDoc,
var_sizes: &'d HashMap<String, usize>,
fields: Vec<(String, FieldPosition)>,
}
@@ -227,7 +227,7 @@ impl<'d> BuildCtx<'d> {
/// top level).
fn walk_struct(
&mut self,
struct_node: &BastStruct<'d>,
struct_node: &BastStruct,
prefix: &str,
offset: &mut usize,
) -> Result<(), AlkTypeError> {
@@ -246,7 +246,7 @@ impl<'d> BuildCtx<'d> {
/// appending any field paths to `self.fields`.
fn walk_field(
&mut self,
field: &BastField<'d>,
field: &BastField,
field_path: &str,
offset: &mut usize,
) -> Result<(), AlkTypeError> {
@@ -330,7 +330,7 @@ impl<'d> BuildCtx<'d> {
/// supported in v1.
fn walk_array(
&mut self,
array: &BastArray<'d>,
array: &BastArray,
field_path: &str,
offset: &mut usize,
) -> Result<(), AlkTypeError> {
@@ -394,7 +394,7 @@ impl<'d> BuildCtx<'d> {
/// Compute the layout for a `BastUnion` field.
fn walk_union(
&mut self,
union_node: &BastUnion<'d>,
union_node: &BastUnion,
field_path: &str,
offset: &mut usize,
) -> Result<(), AlkTypeError> {
@@ -415,7 +415,7 @@ impl<'d> BuildCtx<'d> {
offset: &mut usize,
disc_type: AlkTypeKind,
disc_off: usize,
union_node: &BastUnion<'d>,
union_node: &BastUnion,
) -> Result<(), AlkTypeError> {
let disc_size = disc_type.type_size().ok_or_else(|| AlkTypeError::Offset {
field_path: field_path.to_string(),
@@ -488,7 +488,7 @@ impl<'d> BuildCtx<'d> {
&mut self,
field_path: &str,
offset: &mut usize,
union_node: &BastUnion<'d>,
union_node: &BastUnion,
) -> Result<(), AlkTypeError> {
let variant_key = format!("{field_path}.{VARIANT_KEY}");
let variant_index =
+3 -2
View File
@@ -9,8 +9,9 @@
//!
//! - **BAST parser** ([`bast`]): Typed tree over a BAST document —
//! `BastDoc`/`BastDef`/`BastStruct`/`BastField`/`BastType`/etc.
//! Borrows from the source `serde_json::Value` without cloning field
//! data.
//! Owns its data (ADR-012 §2a) so consumers (`AlkTypeEngine`,
//! `LayoutBuilder`) can store the parsed tree without lifetime
//! entanglement; the clone happens once at `BastDoc::new`.
//! - **Layout engine** ([`offset_map`], [`layout_builder`],
//! [`sequential_reader`], [`read_plan`]): Two layout modes — aligned
//! static for mmap-friendly formats, packed sequential for protocol
+27 -67
View File
@@ -460,7 +460,7 @@ fn materialize_plan_record(
/// from the same BAST document. Endianness is read from the root struct's
/// `endian` annotation (defaults to little-endian).
pub fn materialize_aligned(
doc: &BastDoc<'_>,
doc: &BastDoc,
buffer: &[u8],
offset_map: &crate::offset_map::OffsetMap,
) -> Result<Value, AlkTypeError> {
@@ -480,8 +480,8 @@ pub fn materialize_aligned(
fn materialize_struct_packed(
doc: &BastDoc<'_>,
struct_node: &BastStruct<'_>,
doc: &BastDoc,
struct_node: &BastStruct,
path_prefix: &str,
endian: Endian,
buffer: &[u8],
@@ -502,8 +502,8 @@ fn materialize_struct_packed(
}
fn materialize_field_packed(
doc: &BastDoc<'_>,
field: &BastField<'_>,
doc: &BastDoc,
field: &BastField,
field_path: &str,
endian: Endian,
buffer: &[u8],
@@ -512,13 +512,12 @@ fn materialize_field_packed(
let field_endian = field.effective_endian(endian);
let ty = field.ty();
let resolved = doc.resolve_typeref(ty)?;
materialize_typeref_packed(doc, &resolved, field, field_path, field_endian, buffer, offset)
materialize_typeref_packed(doc, &resolved, field_path, field_endian, buffer, offset)
}
fn materialize_typeref_packed(
doc: &BastDoc<'_>,
ty: &BastType<'_>,
_field: &BastField<'_>,
doc: &BastDoc,
ty: &BastType,
field_path: &str,
endian: Endian,
buffer: &[u8],
@@ -626,8 +625,8 @@ fn materialize_typeref_packed(
}
fn materialize_array_packed(
doc: &BastDoc<'_>,
array: &BastArray<'_>,
doc: &BastDoc,
array: &BastArray,
field_path: &str,
endian: Endian,
buffer: &[u8],
@@ -642,7 +641,6 @@ fn materialize_array_packed(
let value = materialize_typeref_packed(
doc,
&resolved_elem,
&dummy_field_for(&resolved_elem),
&path,
endian,
buffer,
@@ -654,8 +652,8 @@ fn materialize_array_packed(
}
fn materialize_union_packed(
doc: &BastDoc<'_>,
union_node: &BastUnion<'_>,
doc: &BastDoc,
union_node: &BastUnion,
field_path: &str,
endian: Endian,
buffer: &[u8],
@@ -709,7 +707,6 @@ fn materialize_union_packed(
let variant_value = materialize_typeref_packed(
doc,
&resolved_variant,
&dummy_field_for(&resolved_variant),
field_path,
endian,
buffer,
@@ -744,7 +741,6 @@ fn materialize_union_packed(
let variant_value = materialize_typeref_packed(
doc,
&resolved_variant,
&dummy_field_for(&resolved_variant),
field_path,
endian,
buffer,
@@ -760,8 +756,8 @@ fn materialize_union_packed(
}
fn materialize_record_packed(
doc: &BastDoc<'_>,
record: &BastRecord<'_>,
doc: &BastDoc,
record: &BastRecord,
field_path: &str,
endian: Endian,
buffer: &[u8],
@@ -784,7 +780,6 @@ fn materialize_record_packed(
let val = materialize_typeref_packed(
doc,
&resolved_values,
&dummy_field_for(&resolved_values),
&val_path,
endian,
buffer,
@@ -842,8 +837,8 @@ fn number_from_f64(f: f64, field_path: &str) -> Result<Value, AlkTypeError> {
/// sequential walks for composites (Record) whose internal layout can't
/// be precomputed into the static offset map.
fn materialize_struct_aligned(
doc: &BastDoc<'_>,
struct_node: &BastStruct<'_>,
doc: &BastDoc,
struct_node: &BastStruct,
path_prefix: &str,
offset_map: &crate::offset_map::OffsetMap,
endian: Endian,
@@ -891,7 +886,6 @@ fn materialize_struct_aligned(
materialize_typeref_packed(
doc,
&resolved,
field,
&path,
field_endian,
buffer,
@@ -917,8 +911,8 @@ fn materialize_struct_aligned(
/// at its `vals[i]` offset-map entry. Array elements are always fixed-size
/// in aligned mode (the offset map rejects variable-length elements).
fn materialize_array_aligned(
doc: &BastDoc<'_>,
array: &BastArray<'_>,
doc: &BastDoc,
array: &BastArray,
field_path: &str,
offset_map: &crate::offset_map::OffsetMap,
endian: Endian,
@@ -949,9 +943,9 @@ fn materialize_array_aligned(
/// trailing NUL bytes are trimmed.
/// - inline length-prefixed (default): a 4-byte length prefix at `start`.
fn materialize_variable_aligned(
doc: &BastDoc<'_>,
field: &BastField<'_>,
ty: &BastType<'_>,
doc: &BastDoc,
field: &BastField,
ty: &BastType,
field_path: &str,
range: &crate::offset_map::ByteRange,
endian: Endian,
@@ -1010,49 +1004,15 @@ fn materialize_variable_aligned(
}
fn materialize_leaf_at(
doc: &BastDoc<'_>,
ty: &BastType<'_>,
doc: &BastDoc,
ty: &BastType,
field_path: &str,
offset: usize,
endian: Endian,
buffer: &[u8],
) -> Result<Value, AlkTypeError> {
let mut pos = offset;
materialize_typeref_packed(
doc,
ty,
&dummy_field_for(ty),
field_path,
endian,
buffer,
&mut pos,
)
}
/// Construct a synthetic `BastField` borrowing from a resolved
/// `BastType`'s source node. Used when materializing array elements /
/// record values / union variants — these are TypeRefs, not fields, so
/// they don't carry field-level annotations. The synthetic field
/// inherits no annotations (which is correct: `maxLength`/`encoding`/
/// `endian`/`align` are field-level properties, and array elements and
/// record values don't have them in BAST).
///
/// The `BastField::parse` path validates the node; this helper bypasses
/// re-validation for a node we've already resolved. The returned field
/// borrows from the `BastType`'s source.
fn dummy_field_for<'d>(ty: &BastType<'d>) -> BastField<'d> {
let source = ty_source(ty);
BastField::synthetic(ty.clone(), source)
}
fn ty_source<'d>(ty: &BastType<'d>) -> &'d serde_json::Value {
match ty {
BastType::Struct(s) => s.source(),
BastType::Union(u) => u.source(),
BastType::Array(a) => a.source(),
BastType::Record(r) => r.source(),
_ => &serde_json::Value::Null,
}
materialize_typeref_packed(doc, ty, field_path, endian, buffer, &mut pos)
}
#[cfg(test)]
@@ -1060,13 +1020,13 @@ mod tests {
use super::*;
use serde_json::json;
fn doc_from<'a>(root: &'a Value, name: &'a str) -> BastDoc<'a> {
fn doc_from(root: &Value, name: &str) -> BastDoc {
BastDoc::new(root, name).expect("bast doc")
}
fn materialize_packed_strict(
root: &Value,
doc: &BastDoc<'_>,
doc: &BastDoc,
buffer: &[u8],
) -> Result<Value, AlkTypeError> {
let plan = ReadPlan::compile(root, doc.root_name())?;
@@ -1457,7 +1417,7 @@ mod tests {
// ----- Aligned-mode materialization (M2 fixes) ----------------------
fn materialize_aligned_strict(
doc: &BastDoc<'_>,
doc: &BastDoc,
buffer: &[u8],
) -> Result<Value, AlkTypeError> {
let offset_map = crate::offset_map::OffsetMap::compute(doc)?;
+13 -13
View File
@@ -76,7 +76,7 @@ impl OffsetMap {
/// Returns [`AlkTypeError::Offset`] for unsupported type combinations
/// encountered during the walk (e.g. unions, which are rejected in
/// aligned mode per ADR-008).
pub fn compute<'a>(doc: &'a BastDoc<'a>) -> Result<Self, AlkTypeError> {
pub fn compute(doc: &BastDoc) -> Result<Self, AlkTypeError> {
let root_def = doc.root_def();
let struct_node = match root_def.kind() {
BastDefKind::Struct(s) => s,
@@ -133,7 +133,7 @@ impl OffsetMap {
/// reference to the BAST doc for `$ref` resolution. Grouping these
/// keeps the recursive helper signatures small.
struct ComputeCtx<'d> {
doc: &'d BastDoc<'d>,
doc: &'d BastDoc,
fields: Vec<(String, ByteRange)>,
offset: usize,
}
@@ -158,7 +158,7 @@ impl<'d> ComputeCtx<'d> {
/// a natural alignment larger than the default.
fn compute_struct(
&mut self,
struct_node: &BastStruct<'d>,
struct_node: &BastStruct,
prefix: &str,
parent_struct_align: usize,
) -> Result<(usize, usize), AlkTypeError> {
@@ -210,7 +210,7 @@ impl<'d> ComputeCtx<'d> {
/// and appending any field paths to `self.fields`.
fn compute_field(
&mut self,
field: &BastField<'d>,
field: &BastField,
field_path: &str,
struct_default_align: usize,
) -> Result<FieldLayout, AlkTypeError> {
@@ -253,7 +253,7 @@ impl<'d> ComputeCtx<'d> {
fn compute_fixed_field(
&mut self,
kind: AlkTypeKind,
field: &BastField<'d>,
field: &BastField,
field_path: &str,
struct_default_align: usize,
) -> Result<FieldLayout, AlkTypeError> {
@@ -282,8 +282,8 @@ impl<'d> ComputeCtx<'d> {
/// then shifts the nested fields to their final positions.
fn compute_struct_field(
&mut self,
struct_node: &BastStruct<'d>,
field: &BastField<'d>,
struct_node: &BastStruct,
field: &BastField,
field_path: &str,
struct_default_align: usize,
) -> Result<FieldLayout, AlkTypeError> {
@@ -321,8 +321,8 @@ impl<'d> ComputeCtx<'d> {
/// Compute the layout for a `BastArray` field.
fn compute_array_field(
&mut self,
array: &BastArray<'d>,
field: &BastField<'d>,
array: &BastArray,
field: &BastField,
field_path: &str,
struct_default_align: usize,
) -> Result<FieldLayout, AlkTypeError> {
@@ -396,7 +396,7 @@ impl<'d> ComputeCtx<'d> {
/// - inline length-prefixing (default): a 4-byte length prefix.
fn compute_variable_field(
&mut self,
field: &BastField<'d>,
field: &BastField,
field_path: &str,
struct_default_align: usize,
) -> Result<FieldLayout, AlkTypeError> {
@@ -430,7 +430,7 @@ impl<'d> ComputeCtx<'d> {
}
/// If the field's type is a variable-length primitive, return its kind.
fn field_variable_kind(field: &BastField<'_>) -> Option<AlkTypeKind> {
fn field_variable_kind(field: &BastField) -> Option<AlkTypeKind> {
match field.ty() {
BastType::Primitive(k) if k.is_variable_length() => Some(*k),
_ => None,
@@ -439,7 +439,7 @@ fn field_variable_kind(field: &BastField<'_>) -> Option<AlkTypeKind> {
/// Resolve the field's alignment: field-level `align` annotation,
/// then the struct default, then the natural alignment.
fn field_alignment(field: &BastField<'_>, struct_default_align: usize, natural: usize) -> usize {
fn field_alignment(field: &BastField, struct_default_align: usize, natural: usize) -> usize {
if let Some(a) = field.align() {
return a.max(1);
}
@@ -452,7 +452,7 @@ fn field_alignment(field: &BastField<'_>, struct_default_align: usize, natural:
/// schemas (BAST field-level annotations live on the field, not the
/// element TypeRef), so we fall back to the struct default.
fn element_alignment(
elem_ty: &BastType<'_>,
elem_ty: &BastType,
struct_default_align: usize,
natural: usize,
) -> usize {
+20 -20
View File
@@ -257,8 +257,8 @@ fn dotted(prefix: &str, name: &str) -> String {
}
fn compile_struct(
doc: &BastDoc<'_>,
s: &BastStruct<'_>,
doc: &BastDoc,
s: &BastStruct,
container_endian: Endian,
path: &str,
depth: usize,
@@ -281,8 +281,8 @@ fn compile_struct(
}
fn compile_field_list(
doc: &BastDoc<'_>,
fields: &[BastField<'_>],
doc: &BastDoc,
fields: &[BastField],
container_endian: Endian,
path: &str,
depth: usize,
@@ -297,8 +297,8 @@ fn compile_field_list(
}
fn compile_field(
doc: &BastDoc<'_>,
field: &BastField<'_>,
doc: &BastDoc,
field: &BastField,
container_endian: Endian,
path: &str,
depth: usize,
@@ -320,8 +320,8 @@ fn compile_field(
/// body, eagerly resolving the ref (with the cycle set guarding the
/// definition-level recursion).
fn compile_typeref(
doc: &BastDoc<'_>,
ty: &BastType<'_>,
doc: &BastDoc,
ty: &BastType,
container_endian: Endian,
path: &str,
depth: usize,
@@ -344,8 +344,8 @@ fn compile_typeref(
/// Compile a dereferenced type node (the `Ref` case never reaches here).
fn compile_resolved(
doc: &BastDoc<'_>,
ty: &BastType<'_>,
doc: &BastDoc,
ty: &BastType,
container_endian: Endian,
path: &str,
depth: usize,
@@ -379,8 +379,8 @@ fn compile_resolved(
/// Compile a named definition's kind (the `$ref` target: struct, union,
/// or enum — the parser rejects other kinds at `$defs` top level).
fn compile_def_kind(
doc: &BastDoc<'_>,
kind: &BastDefKind<'_>,
doc: &BastDoc,
kind: &BastDefKind,
container_endian: Endian,
path: &str,
depth: usize,
@@ -400,8 +400,8 @@ fn compile_def_kind(
}
fn compile_union(
doc: &BastDoc<'_>,
u: &BastUnion<'_>,
doc: &BastDoc,
u: &BastUnion,
container_endian: Endian,
path: &str,
depth: usize,
@@ -462,8 +462,8 @@ fn compile_union(
/// union (mirroring the 0.2.0 read loop's `resolve_and_walk_variant`);
/// a `$ref` variant resolves through the cycle set.
fn compile_variant(
doc: &BastDoc<'_>,
ty: &BastType<'_>,
doc: &BastDoc,
ty: &BastType,
container_endian: Endian,
path: &str,
depth: usize,
@@ -515,8 +515,8 @@ fn compile_variant(
}
fn compile_array(
doc: &BastDoc<'_>,
a: &BastArray<'_>,
doc: &BastDoc,
a: &BastArray,
container_endian: Endian,
path: &str,
depth: usize,
@@ -537,8 +537,8 @@ fn compile_array(
}
fn compile_record(
doc: &BastDoc<'_>,
r: &BastRecord<'_>,
doc: &BastDoc,
r: &BastRecord,
container_endian: Endian,
path: &str,
depth: usize,
+8 -8
View File
@@ -46,7 +46,7 @@ pub struct UnionDispatch {
/// `mapping`.
pub fn read_byte_discriminator(
buffer: &[u8],
union_node: &BastUnion<'_>,
union_node: &BastUnion,
endian: Endian,
) -> Result<UnionDispatch, AlkTypeError> {
let (offset, disc_type) = match union_node.discriminator() {
@@ -112,12 +112,12 @@ pub fn read_byte_discriminator(
/// union's `mapping`.
pub fn read_field_discriminator(
buffer: &[u8],
union_node: &BastUnion<'_>,
union_node: &BastUnion,
disc_field_offset: usize,
endian: Endian,
) -> Result<UnionDispatch, AlkTypeError> {
let name = match union_node.discriminator() {
BastDiscriminator::Field { name } => *name,
BastDiscriminator::Field { name } => name.as_str(),
BastDiscriminator::Byte { .. } => {
return Err(AlkTypeError::Schema(
"read_field_discriminator requires a field-name discriminator".to_string(),
@@ -193,9 +193,9 @@ pub fn read_field_discriminator(
/// - [`AlkTypeError::Schema`] if the union has no `mapping` entries or
/// the `key` is not present.
pub fn resolve_variant<'a>(
union_node: &'a BastUnion<'a>,
union_node: &'a BastUnion,
key: &str,
) -> Result<&'a BastType<'a>, AlkTypeError> {
) -> Result<&'a BastType, AlkTypeError> {
union_node
.variant_for(key)
.ok_or_else(|| AlkTypeError::Schema(format!("unknown mapping key: {key}")))
@@ -211,7 +211,7 @@ pub fn resolve_variant<'a>(
///
/// - [`AlkTypeError::Schema`] if the discriminator is a field-name
/// discriminator.
pub fn discriminator_size(union_node: &BastUnion<'_>) -> Result<usize, AlkTypeError> {
pub fn discriminator_size(union_node: &BastUnion) -> Result<usize, AlkTypeError> {
match union_node.discriminator() {
BastDiscriminator::Byte { disc_type, .. } => match disc_type {
AlkTypeKind::Uint8 => Ok(1),
@@ -228,7 +228,7 @@ pub fn discriminator_size(union_node: &BastUnion<'_>) -> Result<usize, AlkTypeEr
}
fn verify_mapping_key(
union_node: &BastUnion<'_>,
union_node: &BastUnion,
key: &str,
field_path: &str,
raw_value: &str,
@@ -252,7 +252,7 @@ mod tests {
const LE: Endian = Endian::Little;
const BE: Endian = Endian::Big;
fn doc_union<'a>(root: &'a serde_json::Value, name: &'a str) -> BastUnion<'a> {
fn doc_union(root: &serde_json::Value, name: &str) -> BastUnion {
let doc = BastDoc::new(root, name).expect("bast doc");
match doc.root_def().kind() {
crate::bast::BastDefKind::Union(u) => u.clone(),
+11 -11
View File
@@ -111,7 +111,7 @@ impl ValidationPlan {
/// nests deeper than the compile depth cap (128) or contains a
/// reference cycle (a compile-time rejection of self-recursive
/// schemas).
pub fn compile(doc: &BastDoc<'_>) -> Result<Self, AlkTypeError> {
pub fn compile(doc: &BastDoc) -> Result<Self, AlkTypeError> {
let root_def = doc.root_def();
let mut seen = BTreeSet::new();
let root = match root_def.kind() {
@@ -255,7 +255,7 @@ impl ValidVariant {
}
}
fn compile_enum_node(e: &crate::bast::BastEnum<'_>) -> Result<ValidNode, AlkTypeError> {
fn compile_enum_node(e: &crate::bast::BastEnum) -> Result<ValidNode, AlkTypeError> {
let count = u64::try_from(e.values().len()).map_err(|_| {
AlkTypeError::Schema("bast_validation: enum values len overflows u64".to_string())
})?;
@@ -280,8 +280,8 @@ fn cycle_err(name: &str, path: &str) -> AlkTypeError {
/// `String`/`Bytes` leaves; never crosses a `$ref`, whose target is
/// always a struct/union/enum `$defs` entry).
fn compile_typeref(
doc: &BastDoc<'_>,
ty: &BastType<'_>,
doc: &BastDoc,
ty: &BastType,
max_len: Option<usize>,
path: &str,
depth: usize,
@@ -311,8 +311,8 @@ fn compile_typeref(
/// Compile a dereferenced type node (the `Ref` case never reaches here).
fn compile_resolved(
doc: &BastDoc<'_>,
ty: &BastType<'_>,
doc: &BastDoc,
ty: &BastType,
max_len: Option<usize>,
path: &str,
depth: usize,
@@ -367,8 +367,8 @@ fn compile_resolved(
}
fn compile_struct(
doc: &BastDoc<'_>,
s: &BastStruct<'_>,
doc: &BastDoc,
s: &BastStruct,
path: &str,
depth: usize,
seen: &mut BTreeSet<String>,
@@ -400,8 +400,8 @@ fn compile_struct(
}
fn compile_union(
doc: &BastDoc<'_>,
u: &BastUnion<'_>,
doc: &BastDoc,
u: &BastUnion,
path: &str,
depth: usize,
seen: &mut BTreeSet<String>,
@@ -692,7 +692,7 @@ mod tests {
use super::*;
use serde_json::json;
fn doc_from<'a>(root: &'a Value, name: &'a str) -> BastDoc<'a> {
fn doc_from(root: &Value, name: &str) -> BastDoc {
BastDoc::new(root, name).expect("bast doc")
}
+1 -1
View File
@@ -49,7 +49,7 @@ fn sftp_like_byte_union_doc() -> serde_json::Value {
})
}
fn union_of<'a>(root: &'a serde_json::Value, name: &'a str) -> BastUnion<'a> {
fn union_of(root: &serde_json::Value, name: &str) -> BastUnion {
let doc = BastDoc::new(root, name).expect("bast doc");
match doc.root_def().kind() {
BastDefKind::Union(u) => u.clone(),