Rebrand TypeDef to AlkType in code, keyword strings, and docs
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.
This commit is contained in:
1 parent
1cfb3638d1
commit
5e268e8f47
31 files changed
+1851
-1851
No files matched your search
+222
-222
@@ -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:<name>` JSON Schema keyword.
|
||||
/// Each variant corresponds to a `AlkType:<name>` 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<usize> {
|
||||
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<Self, Self::Err> {
|
||||
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<TypeDefKind> {
|
||||
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<AlkTypeKind> {
|
||||
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<DiscriminatorKind, TypedefError> {
|
||||
pub fn parse_discriminator(node: &Value) -> Result<DiscriminatorKind, AlkTypeError> {
|
||||
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<DiscriminatorKind, TypedefErr
|
||||
.get("name")
|
||||
.and_then(Value::as_str)
|
||||
.ok_or_else(|| {
|
||||
TypedefError::Schema(
|
||||
AlkTypeError::Schema(
|
||||
"field discriminator is missing required 'name' field".to_string(),
|
||||
)
|
||||
})?
|
||||
.to_string();
|
||||
Ok(DiscriminatorKind::Field { name })
|
||||
}
|
||||
other => 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<TypeDefKind> {
|
||||
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<AlkTypeKind> {
|
||||
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::<TypeDefKind>().is_err());
|
||||
assert!("TypeDef:Int128".parse::<TypeDefKind>().is_err());
|
||||
assert!("not-a-typedef".parse::<TypeDefKind>().is_err());
|
||||
assert!("AlkType:Uint128".parse::<AlkTypeKind>().is_err());
|
||||
assert!("AlkType:Int128".parse::<AlkTypeKind>().is_err());
|
||||
assert!("not-an-alktype".parse::<AlkTypeKind>().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"
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user