style(typedef): apply cargo fmt across all source and test files
Resolve formatting differences flagged by cargo fmt --check during the review checkpoint. No functional changes.
This commit is contained in:
1 parent
bf868157d3
commit
0d39b7f12a
14 files changed
+744
-422
No files matched your search
@@ -60,12 +60,18 @@ fn read_array<const N: usize>(
|
||||
let slice = buffer.get(offset..end).ok_or_else(|| {
|
||||
access_err(
|
||||
field_path,
|
||||
format!("slice [{offset}..{end}) unavailable in buffer of length {}", buffer.len()),
|
||||
format!(
|
||||
"slice [{offset}..{end}) unavailable in buffer of length {}",
|
||||
buffer.len()
|
||||
),
|
||||
)
|
||||
})?;
|
||||
slice
|
||||
.try_into()
|
||||
.map_err(|_| access_err(field_path, format!("internal: try_into failed for {N}-byte slice")))
|
||||
slice.try_into().map_err(|_| {
|
||||
access_err(
|
||||
field_path,
|
||||
format!("internal: try_into failed for {N}-byte slice"),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn write_array<const N: usize>(
|
||||
@@ -379,7 +385,12 @@ pub fn write_bool(
|
||||
value: bool,
|
||||
field_path: &str,
|
||||
) -> Result<(), TypedefError> {
|
||||
write_array(buffer, offset, [if value { 0x01 } else { 0x00 }], field_path)
|
||||
write_array(
|
||||
buffer,
|
||||
offset,
|
||||
[if value { 0x01 } else { 0x00 }],
|
||||
field_path,
|
||||
)
|
||||
}
|
||||
|
||||
/// Write a `TEnum` index (`u32`) `value` at `offset` into `buffer`, applying `endian`.
|
||||
@@ -536,12 +547,9 @@ pub fn read_bytes_indirect<'a>(
|
||||
field_path: &str,
|
||||
endian: Endian,
|
||||
) -> Result<&'a [u8], TypedefError> {
|
||||
let struct_end = offset.checked_add(8).ok_or_else(|| {
|
||||
access_err(
|
||||
field_path,
|
||||
format!("offset {offset} + 8 overflows usize"),
|
||||
)
|
||||
})?;
|
||||
let struct_end = offset
|
||||
.checked_add(8)
|
||||
.ok_or_else(|| access_err(field_path, format!("offset {offset} + 8 overflows usize")))?;
|
||||
check_bounds(buffer.len(), offset, struct_end, field_path)?;
|
||||
let off_bytes: [u8; U32_SIZE] = buffer[offset..offset + U32_SIZE]
|
||||
.try_into()
|
||||
@@ -639,11 +647,17 @@ mod tests {
|
||||
let value: f32 = std::f32::consts::PI;
|
||||
write_f32(&mut buf, 0, value, "f", LE).unwrap();
|
||||
let read = read_f32(&buf, 0, "f", LE).unwrap();
|
||||
assert!((read - value).abs() < 1e-6, "le mismatch: {read} vs {value}");
|
||||
assert!(
|
||||
(read - value).abs() < 1e-6,
|
||||
"le mismatch: {read} vs {value}"
|
||||
);
|
||||
|
||||
write_f32(&mut buf, 0, value, "f", BE).unwrap();
|
||||
let read = read_f32(&buf, 0, "f", BE).unwrap();
|
||||
assert!((read - value).abs() < 1e-6, "be mismatch: {read} vs {value}");
|
||||
assert!(
|
||||
(read - value).abs() < 1e-6,
|
||||
"be mismatch: {read} vs {value}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -827,4 +841,4 @@ mod tests {
|
||||
let bytes = read_bytes(&buf, 0, "data", LE).unwrap();
|
||||
assert!(bytes.is_empty());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,9 +44,7 @@ enum Layout {
|
||||
},
|
||||
/// Aligned static layout. Field offsets are precomputed in an
|
||||
/// [`OffsetMap`] for random access.
|
||||
Aligned {
|
||||
offset_map: OffsetMap,
|
||||
},
|
||||
Aligned { offset_map: OffsetMap },
|
||||
}
|
||||
|
||||
/// The compiled form of a typedef schema. Combines the layout engine
|
||||
@@ -84,10 +82,7 @@ impl TypedefEngine {
|
||||
/// 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<Self, TypedefError> {
|
||||
pub fn compile(schema: &mut Value, mode: LayoutMode) -> Result<Self, TypedefError> {
|
||||
schema::normalize_refs(schema);
|
||||
let endian = Endian::from_schema(schema);
|
||||
let layout = match mode {
|
||||
@@ -200,10 +195,12 @@ impl TypedefEngine {
|
||||
});
|
||||
}
|
||||
};
|
||||
let range = offset_map.get(field_path).ok_or_else(|| TypedefError::Offset {
|
||||
field_path: field_path.to_string(),
|
||||
reason: "field not found in offset map".to_string(),
|
||||
})?;
|
||||
let range = offset_map
|
||||
.get(field_path)
|
||||
.ok_or_else(|| TypedefError::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 {
|
||||
field_path: field_path.to_string(),
|
||||
@@ -275,14 +272,12 @@ impl TypedefEngine {
|
||||
start: range.start,
|
||||
end: range.end,
|
||||
}),
|
||||
"TypeDef:Union" | "TypeDef:Array" | "TypeDef:Record" => {
|
||||
Err(TypedefError::Access {
|
||||
field_path: field_path.to_string(),
|
||||
reason: "read_field does not support composite types; \
|
||||
"TypeDef:Union" | "TypeDef:Array" | "TypeDef:Record" => Err(TypedefError::Access {
|
||||
field_path: field_path.to_string(),
|
||||
reason: "read_field does not support composite types; \
|
||||
use the layout-specific APIs"
|
||||
.to_string(),
|
||||
})
|
||||
}
|
||||
.to_string(),
|
||||
}),
|
||||
other => Err(TypedefError::Access {
|
||||
field_path: field_path.to_string(),
|
||||
reason: format!("unsupported TypeDef kind for read_field: {other}"),
|
||||
@@ -324,24 +319,22 @@ impl TypedefEngine {
|
||||
});
|
||||
}
|
||||
};
|
||||
let range = offset_map.get(field_path).ok_or_else(|| TypedefError::Offset {
|
||||
field_path: field_path.to_string(),
|
||||
reason: "field not found in offset map".to_string(),
|
||||
})?;
|
||||
let range = offset_map
|
||||
.get(field_path)
|
||||
.ok_or_else(|| TypedefError::Offset {
|
||||
field_path: field_path.to_string(),
|
||||
reason: "field not found in offset map".to_string(),
|
||||
})?;
|
||||
let endian = self.endian;
|
||||
match value {
|
||||
FieldValue::I8(v) => {
|
||||
data_access::write_i8(buffer, range.start, *v, field_path)
|
||||
}
|
||||
FieldValue::I8(v) => data_access::write_i8(buffer, range.start, *v, field_path),
|
||||
FieldValue::I16(v) => {
|
||||
data_access::write_i16(buffer, range.start, *v, field_path, endian)
|
||||
}
|
||||
FieldValue::I32(v) => {
|
||||
data_access::write_i32(buffer, range.start, *v, field_path, endian)
|
||||
}
|
||||
FieldValue::U8(v) => {
|
||||
data_access::write_u8(buffer, range.start, *v, field_path)
|
||||
}
|
||||
FieldValue::U8(v) => data_access::write_u8(buffer, range.start, *v, field_path),
|
||||
FieldValue::U16(v) => {
|
||||
data_access::write_u16(buffer, range.start, *v, field_path, endian)
|
||||
}
|
||||
@@ -357,9 +350,7 @@ impl TypedefEngine {
|
||||
FieldValue::F64(v) => {
|
||||
data_access::write_f64(buffer, range.start, *v, field_path, endian)
|
||||
}
|
||||
FieldValue::Bool(v) => {
|
||||
data_access::write_bool(buffer, range.start, *v, field_path)
|
||||
}
|
||||
FieldValue::Bool(v) => data_access::write_bool(buffer, range.start, *v, field_path),
|
||||
FieldValue::Enum(v) => {
|
||||
data_access::write_enum(buffer, range.start, *v, field_path, endian)
|
||||
}
|
||||
@@ -371,14 +362,14 @@ impl TypedefEngine {
|
||||
data_access::write_bytes(buffer, range.start, v, field_path, endian)?;
|
||||
Ok(())
|
||||
}
|
||||
FieldValue::Struct { .. }
|
||||
| FieldValue::Union { .. }
|
||||
| FieldValue::Array { .. } => Err(TypedefError::Access {
|
||||
field_path: field_path.to_string(),
|
||||
reason: "write_field does not support composite types; \
|
||||
FieldValue::Struct { .. } | FieldValue::Union { .. } | FieldValue::Array { .. } => {
|
||||
Err(TypedefError::Access {
|
||||
field_path: field_path.to_string(),
|
||||
reason: "write_field does not support composite types; \
|
||||
use the layout-specific APIs"
|
||||
.to_string(),
|
||||
}),
|
||||
.to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -404,10 +395,7 @@ impl fmt::Debug for TypedefEngine {
|
||||
fn lookup_field_schema<'a>(schema: &'a Value, field_path: &str) -> Option<&'a Value> {
|
||||
let mut current = schema;
|
||||
for segment in field_path.split('.') {
|
||||
current = current
|
||||
.as_object()?
|
||||
.get("properties")?
|
||||
.get(segment)?;
|
||||
current = current.as_object()?.get("properties")?.get(segment)?;
|
||||
}
|
||||
Some(current)
|
||||
}
|
||||
@@ -723,11 +711,7 @@ mod tests {
|
||||
let engine = TypedefEngine::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 },
|
||||
)
|
||||
.write_field(&mut buf, "id", &FieldValue::Struct { start: 0, end: 4 })
|
||||
.unwrap_err();
|
||||
assert!(matches!(err, TypedefError::Access { .. }), "got {err:?}");
|
||||
}
|
||||
@@ -772,4 +756,4 @@ mod tests {
|
||||
let node = json!({ "TypeDef:String": { "encoding": "offset-indirect" } });
|
||||
assert_eq!(typedef_kind_loose(&node), Some("TypeDef:String"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,17 +15,11 @@ pub enum TypedefError {
|
||||
|
||||
/// Offset computation errors — field not found, type not supported
|
||||
/// for offset computation, recursive depth exceeded.
|
||||
Offset {
|
||||
field_path: String,
|
||||
reason: String,
|
||||
},
|
||||
Offset { field_path: String, reason: String },
|
||||
|
||||
/// Read/write errors — buffer too short, invalid UTF-8, value out
|
||||
/// of range for the target type.
|
||||
Access {
|
||||
field_path: String,
|
||||
reason: String,
|
||||
},
|
||||
Access { field_path: String, reason: String },
|
||||
|
||||
/// Validation errors — delegated to the `jsonschema` crate.
|
||||
/// The `'static` lifetime is correct: the validator owns its schema
|
||||
@@ -48,4 +42,4 @@ impl fmt::Display for TypedefError {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for TypedefError {}
|
||||
impl std::error::Error for TypedefError {}
|
||||
@@ -183,10 +183,7 @@ impl LayoutBuilder {
|
||||
/// - [`TypedefError::Offset`] for missing variable-length field
|
||||
/// sizes in `var_sizes`, missing discriminator values, or unknown
|
||||
/// discriminator values.
|
||||
pub fn build(
|
||||
&self,
|
||||
var_sizes: &HashMap<String, usize>,
|
||||
) -> Result<PackedLayout, TypedefError> {
|
||||
pub fn build(&self, var_sizes: &HashMap<String, usize>) -> Result<PackedLayout, TypedefError> {
|
||||
let mut ctx = BuildCtx {
|
||||
root: &self.schema,
|
||||
var_sizes,
|
||||
@@ -298,12 +295,14 @@ impl<'a> BuildCtx<'a> {
|
||||
offset: &mut usize,
|
||||
kind: &str,
|
||||
) -> Result<(), TypedefError> {
|
||||
let data_size = self.var_sizes.get(field_path).copied().ok_or_else(|| {
|
||||
TypedefError::Offset {
|
||||
field_path: field_path.to_string(),
|
||||
reason: "missing variable-length field size".to_string(),
|
||||
}
|
||||
})?;
|
||||
let data_size =
|
||||
self.var_sizes
|
||||
.get(field_path)
|
||||
.copied()
|
||||
.ok_or_else(|| TypedefError::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)
|
||||
@@ -328,21 +327,22 @@ impl<'a> BuildCtx<'a> {
|
||||
field_path: &str,
|
||||
offset: &mut usize,
|
||||
) -> Result<(), TypedefError> {
|
||||
let obj = field_schema.as_object().ok_or_else(|| TypedefError::Offset {
|
||||
field_path: field_path.to_string(),
|
||||
reason: "array schema is not an object".to_string(),
|
||||
})?;
|
||||
let obj = field_schema
|
||||
.as_object()
|
||||
.ok_or_else(|| TypedefError::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 {
|
||||
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 {
|
||||
let element_schema =
|
||||
resolve_ref_or_inline(items, self.root).ok_or_else(|| TypedefError::Offset {
|
||||
field_path: field_path.to_string(),
|
||||
reason: "could not resolve TArray items schema".to_string(),
|
||||
}
|
||||
})?;
|
||||
})?;
|
||||
let elem_kind = typedef_kind_loose(element_schema).ok_or_else(|| TypedefError::Offset {
|
||||
field_path: field_path.to_string(),
|
||||
reason: "TArray element schema has no TypeDef:* kind".to_string(),
|
||||
@@ -362,8 +362,14 @@ impl<'a> BuildCtx<'a> {
|
||||
reason: format!("element kind {elem_kind} has no fixed size"),
|
||||
})?;
|
||||
|
||||
let min_items = obj.get("minItems").and_then(Value::as_u64).map(|n| n as usize);
|
||||
let max_items = obj.get("maxItems").and_then(Value::as_u64).map(|n| n as usize);
|
||||
let min_items = obj
|
||||
.get("minItems")
|
||||
.and_then(Value::as_u64)
|
||||
.map(|n| n as usize);
|
||||
let max_items = obj
|
||||
.get("maxItems")
|
||||
.and_then(Value::as_u64)
|
||||
.map(|n| n as usize);
|
||||
let fixed_count = match (min_items, max_items) {
|
||||
(Some(mn), Some(mx)) if mn == mx => Some(mn),
|
||||
_ => None,
|
||||
@@ -373,15 +379,25 @@ impl<'a> BuildCtx<'a> {
|
||||
let start = *offset;
|
||||
for i in 0..count {
|
||||
let elem_offset = start
|
||||
.checked_add(i.checked_mul(elem_size).ok_or_else(|| TypedefError::Offset {
|
||||
field_path: field_path.to_string(),
|
||||
reason: format!("element index {i} × size {elem_size} overflows usize"),
|
||||
})?)
|
||||
.checked_add(
|
||||
i.checked_mul(elem_size)
|
||||
.ok_or_else(|| TypedefError::Offset {
|
||||
field_path: field_path.to_string(),
|
||||
reason: format!(
|
||||
"element index {i} × size {elem_size} overflows usize"
|
||||
),
|
||||
})?,
|
||||
)
|
||||
.ok_or_else(|| TypedefError::Offset {
|
||||
field_path: field_path.to_string(),
|
||||
reason: format!("element offset {start} + {i}×{elem_size} overflows usize"),
|
||||
})?;
|
||||
self.push(&format!("{field_path}[{i}]"), elem_offset, elem_size, elem_kind);
|
||||
self.push(
|
||||
&format!("{field_path}[{i}]"),
|
||||
elem_offset,
|
||||
elem_size,
|
||||
elem_kind,
|
||||
);
|
||||
}
|
||||
let array_size = count
|
||||
.checked_mul(elem_size)
|
||||
@@ -397,18 +413,22 @@ impl<'a> BuildCtx<'a> {
|
||||
})?;
|
||||
Ok(())
|
||||
} else {
|
||||
let data_size = self.var_sizes.get(field_path).copied().ok_or_else(|| {
|
||||
TypedefError::Offset {
|
||||
field_path: field_path.to_string(),
|
||||
reason: "missing variable-count array element data size".to_string(),
|
||||
}
|
||||
})?;
|
||||
let data_size =
|
||||
self.var_sizes
|
||||
.get(field_path)
|
||||
.copied()
|
||||
.ok_or_else(|| TypedefError::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 {
|
||||
field_path: field_path.to_string(),
|
||||
reason: format!("count prefix {U32_SIZE} + data size {data_size} overflows usize"),
|
||||
reason: format!(
|
||||
"count prefix {U32_SIZE} + data size {data_size} overflows usize"
|
||||
),
|
||||
})?;
|
||||
*offset = start
|
||||
.checked_add(total)
|
||||
@@ -439,15 +459,11 @@ impl<'a> BuildCtx<'a> {
|
||||
})?;
|
||||
|
||||
match disc {
|
||||
DiscriminatorKind::Byte { offset: disc_off, disc_type } => {
|
||||
self.walk_byte_discriminator_union(
|
||||
field_path,
|
||||
offset,
|
||||
&disc_type,
|
||||
disc_off,
|
||||
mapping,
|
||||
)
|
||||
}
|
||||
DiscriminatorKind::Byte {
|
||||
offset: disc_off,
|
||||
disc_type,
|
||||
} => self
|
||||
.walk_byte_discriminator_union(field_path, offset, &disc_type, disc_off, mapping),
|
||||
DiscriminatorKind::Field { name: _ } => {
|
||||
self.walk_field_discriminator_union(field_path, offset, mapping)
|
||||
}
|
||||
@@ -469,27 +485,34 @@ impl<'a> BuildCtx<'a> {
|
||||
})?;
|
||||
|
||||
let disc_key = format!("{field_path}.{DISCRIMINATOR_PATH}");
|
||||
let disc_value = self.var_sizes.get(&disc_key).copied().ok_or_else(|| {
|
||||
TypedefError::Offset {
|
||||
field_path: field_path.to_string(),
|
||||
reason: format!("missing discriminator value at key '{disc_key}'"),
|
||||
}
|
||||
})?;
|
||||
let disc_value =
|
||||
self.var_sizes
|
||||
.get(&disc_key)
|
||||
.copied()
|
||||
.ok_or_else(|| TypedefError::Offset {
|
||||
field_path: field_path.to_string(),
|
||||
reason: format!("missing discriminator value at key '{disc_key}'"),
|
||||
})?;
|
||||
|
||||
let union_start = *offset;
|
||||
let disc_abs_offset = union_start
|
||||
.checked_add(disc_off)
|
||||
.ok_or_else(|| TypedefError::Offset {
|
||||
field_path: field_path.to_string(),
|
||||
reason: format!("union offset {union_start} + disc offset {disc_off} overflows usize"),
|
||||
})?;
|
||||
let disc_abs_offset =
|
||||
union_start
|
||||
.checked_add(disc_off)
|
||||
.ok_or_else(|| TypedefError::Offset {
|
||||
field_path: field_path.to_string(),
|
||||
reason: format!(
|
||||
"union offset {union_start} + disc offset {disc_off} overflows usize"
|
||||
),
|
||||
})?;
|
||||
self.push(&disc_key, disc_abs_offset, disc_size, disc_type);
|
||||
|
||||
let variant_key = disc_value.to_string();
|
||||
let variant_schema = mapping.get(&variant_key).ok_or_else(|| TypedefError::Offset {
|
||||
field_path: field_path.to_string(),
|
||||
reason: format!("unknown discriminator value: {variant_key}"),
|
||||
})?;
|
||||
let variant_schema = mapping
|
||||
.get(&variant_key)
|
||||
.ok_or_else(|| TypedefError::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 {
|
||||
field_path: field_path.to_string(),
|
||||
@@ -507,14 +530,15 @@ impl<'a> BuildCtx<'a> {
|
||||
});
|
||||
}
|
||||
|
||||
let variant_start = disc_abs_offset
|
||||
.checked_add(disc_size)
|
||||
.ok_or_else(|| TypedefError::Offset {
|
||||
field_path: field_path.to_string(),
|
||||
reason: format!(
|
||||
"variant start {disc_abs_offset} + disc size {disc_size} overflows usize"
|
||||
),
|
||||
})?;
|
||||
let variant_start =
|
||||
disc_abs_offset
|
||||
.checked_add(disc_size)
|
||||
.ok_or_else(|| TypedefError::Offset {
|
||||
field_path: field_path.to_string(),
|
||||
reason: format!(
|
||||
"variant start {disc_abs_offset} + disc size {disc_size} overflows usize"
|
||||
),
|
||||
})?;
|
||||
*offset = variant_start;
|
||||
self.walk_struct(resolved, field_path, offset)?;
|
||||
Ok(())
|
||||
@@ -532,21 +556,25 @@ impl<'a> BuildCtx<'a> {
|
||||
mapping: &serde_json::Map<String, Value>,
|
||||
) -> Result<(), TypedefError> {
|
||||
let variant_key = format!("{field_path}.{VARIANT_KEY}");
|
||||
let variant_index = self.var_sizes.get(&variant_key).copied().ok_or_else(|| {
|
||||
TypedefError::Offset {
|
||||
field_path: field_path.to_string(),
|
||||
reason: format!("missing variant index at key '{variant_key}'"),
|
||||
}
|
||||
})?;
|
||||
let variant_entry = mapping.iter().nth(variant_index).ok_or_else(|| {
|
||||
TypedefError::Offset {
|
||||
field_path: field_path.to_string(),
|
||||
reason: format!(
|
||||
"variant index {variant_index} out of range (mapping has {} entries)",
|
||||
mapping.len()
|
||||
),
|
||||
}
|
||||
})?;
|
||||
let variant_index =
|
||||
self.var_sizes
|
||||
.get(&variant_key)
|
||||
.copied()
|
||||
.ok_or_else(|| TypedefError::Offset {
|
||||
field_path: field_path.to_string(),
|
||||
reason: format!("missing variant index at key '{variant_key}'"),
|
||||
})?;
|
||||
let variant_entry =
|
||||
mapping
|
||||
.iter()
|
||||
.nth(variant_index)
|
||||
.ok_or_else(|| TypedefError::Offset {
|
||||
field_path: field_path.to_string(),
|
||||
reason: format!(
|
||||
"variant index {variant_index} out of range (mapping has {} entries)",
|
||||
mapping.len()
|
||||
),
|
||||
})?;
|
||||
let variant_schema = variant_entry.1;
|
||||
let resolved = resolve_ref_or_inline(variant_schema, self.root).ok_or_else(|| {
|
||||
TypedefError::Offset {
|
||||
@@ -675,15 +703,27 @@ mod tests {
|
||||
let layout = build(&schema, &var_sizes(&[]));
|
||||
assert_eq!(
|
||||
layout.get("a"),
|
||||
Some(&FieldPosition { offset: 0, size: 1, kind: "TypeDef:Uint8".into() })
|
||||
Some(&FieldPosition {
|
||||
offset: 0,
|
||||
size: 1,
|
||||
kind: "TypeDef:Uint8".into()
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
layout.get("b"),
|
||||
Some(&FieldPosition { offset: 1, size: 4, kind: "TypeDef:Uint32".into() })
|
||||
Some(&FieldPosition {
|
||||
offset: 1,
|
||||
size: 4,
|
||||
kind: "TypeDef:Uint32".into()
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
layout.get("c"),
|
||||
Some(&FieldPosition { offset: 5, size: 2, kind: "TypeDef:Uint16".into() })
|
||||
Some(&FieldPosition {
|
||||
offset: 5,
|
||||
size: 2,
|
||||
kind: "TypeDef:Uint16".into()
|
||||
})
|
||||
);
|
||||
assert_eq!(layout.total_size(), 7);
|
||||
}
|
||||
@@ -701,15 +741,27 @@ mod tests {
|
||||
let layout = build(&schema, &var_sizes(&[("payload", 10)]));
|
||||
assert_eq!(
|
||||
layout.get("flag"),
|
||||
Some(&FieldPosition { offset: 0, size: 1, kind: "TypeDef:Uint8".into() })
|
||||
Some(&FieldPosition {
|
||||
offset: 0,
|
||||
size: 1,
|
||||
kind: "TypeDef:Uint8".into()
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
layout.get("id"),
|
||||
Some(&FieldPosition { offset: 1, size: 4, kind: "TypeDef:Uint32".into() })
|
||||
Some(&FieldPosition {
|
||||
offset: 1,
|
||||
size: 4,
|
||||
kind: "TypeDef:Uint32".into()
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
layout.get("payload"),
|
||||
Some(&FieldPosition { offset: 5, size: 4, kind: "TypeDef:String".into() })
|
||||
Some(&FieldPosition {
|
||||
offset: 5,
|
||||
size: 4,
|
||||
kind: "TypeDef:String".into()
|
||||
})
|
||||
);
|
||||
assert_eq!(layout.total_size(), 19);
|
||||
}
|
||||
@@ -726,11 +778,19 @@ mod tests {
|
||||
let layout = build(&schema, &var_sizes(&[("name", 5)]));
|
||||
assert_eq!(
|
||||
layout.get("name"),
|
||||
Some(&FieldPosition { offset: 0, size: 4, kind: "TypeDef:String".into() })
|
||||
Some(&FieldPosition {
|
||||
offset: 0,
|
||||
size: 4,
|
||||
kind: "TypeDef:String".into()
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
layout.get("tail"),
|
||||
Some(&FieldPosition { offset: 9, size: 1, kind: "TypeDef:Uint8".into() })
|
||||
Some(&FieldPosition {
|
||||
offset: 9,
|
||||
size: 1,
|
||||
kind: "TypeDef:Uint8".into()
|
||||
})
|
||||
);
|
||||
assert_eq!(layout.total_size(), 10);
|
||||
}
|
||||
@@ -746,7 +806,11 @@ mod tests {
|
||||
let layout = build(&schema, &var_sizes(&[("blob", 3)]));
|
||||
assert_eq!(
|
||||
layout.get("blob"),
|
||||
Some(&FieldPosition { offset: 0, size: 4, kind: "TypeDef:Bytes".into() })
|
||||
Some(&FieldPosition {
|
||||
offset: 0,
|
||||
size: 4,
|
||||
kind: "TypeDef:Bytes".into()
|
||||
})
|
||||
);
|
||||
assert_eq!(layout.total_size(), 7);
|
||||
}
|
||||
@@ -762,7 +826,11 @@ mod tests {
|
||||
let layout = build(&schema, &var_sizes(&[("ts", 20)]));
|
||||
assert_eq!(
|
||||
layout.get("ts"),
|
||||
Some(&FieldPosition { offset: 0, size: 4, kind: "TypeDef:Timestamp".into() })
|
||||
Some(&FieldPosition {
|
||||
offset: 0,
|
||||
size: 4,
|
||||
kind: "TypeDef:Timestamp".into()
|
||||
})
|
||||
);
|
||||
assert_eq!(layout.total_size(), 24);
|
||||
}
|
||||
@@ -781,7 +849,11 @@ mod tests {
|
||||
let layout = build(&schema, &var_sizes(&[("counts", 100)]));
|
||||
assert_eq!(
|
||||
layout.get("counts"),
|
||||
Some(&FieldPosition { offset: 0, size: 4, kind: "TypeDef:Record".into() })
|
||||
Some(&FieldPosition {
|
||||
offset: 0,
|
||||
size: 4,
|
||||
kind: "TypeDef:Record".into()
|
||||
})
|
||||
);
|
||||
assert_eq!(layout.total_size(), 104);
|
||||
}
|
||||
@@ -799,7 +871,10 @@ mod tests {
|
||||
match err {
|
||||
TypedefError::Offset { field_path, reason } => {
|
||||
assert_eq!(field_path, "name");
|
||||
assert!(reason.contains("missing variable-length field size"), "reason: {reason}");
|
||||
assert!(
|
||||
reason.contains("missing variable-length field size"),
|
||||
"reason: {reason}"
|
||||
);
|
||||
}
|
||||
other => panic!("expected Offset, got {other:?}"),
|
||||
}
|
||||
@@ -823,15 +898,27 @@ mod tests {
|
||||
let layout = build(&schema, &var_sizes(&[]));
|
||||
assert_eq!(
|
||||
layout.get("header.magic"),
|
||||
Some(&FieldPosition { offset: 0, size: 4, kind: "TypeDef:Uint32".into() })
|
||||
Some(&FieldPosition {
|
||||
offset: 0,
|
||||
size: 4,
|
||||
kind: "TypeDef:Uint32".into()
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
layout.get("header.version"),
|
||||
Some(&FieldPosition { offset: 4, size: 1, kind: "TypeDef:Uint8".into() })
|
||||
Some(&FieldPosition {
|
||||
offset: 4,
|
||||
size: 1,
|
||||
kind: "TypeDef:Uint8".into()
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
layout.get("body"),
|
||||
Some(&FieldPosition { offset: 5, size: 4, kind: "TypeDef:Uint32".into() })
|
||||
Some(&FieldPosition {
|
||||
offset: 5,
|
||||
size: 4,
|
||||
kind: "TypeDef:Uint32".into()
|
||||
})
|
||||
);
|
||||
assert_eq!(layout.total_size(), 9);
|
||||
}
|
||||
@@ -854,15 +941,27 @@ mod tests {
|
||||
let layout = build(&schema, &var_sizes(&[("header.name", 3)]));
|
||||
assert_eq!(
|
||||
layout.get("header.id"),
|
||||
Some(&FieldPosition { offset: 0, size: 1, kind: "TypeDef:Uint8".into() })
|
||||
Some(&FieldPosition {
|
||||
offset: 0,
|
||||
size: 1,
|
||||
kind: "TypeDef:Uint8".into()
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
layout.get("header.name"),
|
||||
Some(&FieldPosition { offset: 1, size: 4, kind: "TypeDef:String".into() })
|
||||
Some(&FieldPosition {
|
||||
offset: 1,
|
||||
size: 4,
|
||||
kind: "TypeDef:String".into()
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
layout.get("tail"),
|
||||
Some(&FieldPosition { offset: 8, size: 1, kind: "TypeDef:Uint8".into() })
|
||||
Some(&FieldPosition {
|
||||
offset: 8,
|
||||
size: 1,
|
||||
kind: "TypeDef:Uint8".into()
|
||||
})
|
||||
);
|
||||
assert_eq!(layout.total_size(), 9);
|
||||
}
|
||||
@@ -883,15 +982,27 @@ mod tests {
|
||||
let layout = build(&schema, &var_sizes(&[]));
|
||||
assert_eq!(
|
||||
layout.get("vals[0]"),
|
||||
Some(&FieldPosition { offset: 0, size: 4, kind: "TypeDef:Uint32".into() })
|
||||
Some(&FieldPosition {
|
||||
offset: 0,
|
||||
size: 4,
|
||||
kind: "TypeDef:Uint32".into()
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
layout.get("vals[1]"),
|
||||
Some(&FieldPosition { offset: 4, size: 4, kind: "TypeDef:Uint32".into() })
|
||||
Some(&FieldPosition {
|
||||
offset: 4,
|
||||
size: 4,
|
||||
kind: "TypeDef:Uint32".into()
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
layout.get("vals[2]"),
|
||||
Some(&FieldPosition { offset: 8, size: 4, kind: "TypeDef:Uint32".into() })
|
||||
Some(&FieldPosition {
|
||||
offset: 8,
|
||||
size: 4,
|
||||
kind: "TypeDef:Uint32".into()
|
||||
})
|
||||
);
|
||||
assert_eq!(layout.total_size(), 12);
|
||||
}
|
||||
@@ -913,15 +1024,27 @@ mod tests {
|
||||
let layout = build(&schema, &var_sizes(&[]));
|
||||
assert_eq!(
|
||||
layout.get("id"),
|
||||
Some(&FieldPosition { offset: 0, size: 1, kind: "TypeDef:Uint8".into() })
|
||||
Some(&FieldPosition {
|
||||
offset: 0,
|
||||
size: 1,
|
||||
kind: "TypeDef:Uint8".into()
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
layout.get("vals[0]"),
|
||||
Some(&FieldPosition { offset: 1, size: 2, kind: "TypeDef:Uint16".into() })
|
||||
Some(&FieldPosition {
|
||||
offset: 1,
|
||||
size: 2,
|
||||
kind: "TypeDef:Uint16".into()
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
layout.get("vals[1]"),
|
||||
Some(&FieldPosition { offset: 3, size: 2, kind: "TypeDef:Uint16".into() })
|
||||
Some(&FieldPosition {
|
||||
offset: 3,
|
||||
size: 2,
|
||||
kind: "TypeDef:Uint16".into()
|
||||
})
|
||||
);
|
||||
assert_eq!(layout.total_size(), 5);
|
||||
}
|
||||
@@ -940,7 +1063,11 @@ mod tests {
|
||||
let layout = build(&schema, &var_sizes(&[("vals", 12)]));
|
||||
assert_eq!(
|
||||
layout.get("vals"),
|
||||
Some(&FieldPosition { offset: 0, size: 4, kind: "TypeDef:Array".into() })
|
||||
Some(&FieldPosition {
|
||||
offset: 0,
|
||||
size: 4,
|
||||
kind: "TypeDef:Array".into()
|
||||
})
|
||||
);
|
||||
assert_eq!(layout.total_size(), 16);
|
||||
}
|
||||
@@ -1022,15 +1149,27 @@ mod tests {
|
||||
let layout = build(&schema, &vs);
|
||||
assert_eq!(
|
||||
layout.get("payload.__discriminator"),
|
||||
Some(&FieldPosition { offset: 0, size: 1, kind: "TypeDef:Uint8".into() })
|
||||
Some(&FieldPosition {
|
||||
offset: 0,
|
||||
size: 1,
|
||||
kind: "TypeDef:Uint8".into()
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
layout.get("payload.handle"),
|
||||
Some(&FieldPosition { offset: 1, size: 4, kind: "TypeDef:Uint32".into() })
|
||||
Some(&FieldPosition {
|
||||
offset: 1,
|
||||
size: 4,
|
||||
kind: "TypeDef:Uint32".into()
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
layout.get("payload.length"),
|
||||
Some(&FieldPosition { offset: 5, size: 4, kind: "TypeDef:Uint32".into() })
|
||||
Some(&FieldPosition {
|
||||
offset: 5,
|
||||
size: 4,
|
||||
kind: "TypeDef:Uint32".into()
|
||||
})
|
||||
);
|
||||
assert_eq!(layout.total_size(), 9);
|
||||
}
|
||||
@@ -1075,11 +1214,19 @@ mod tests {
|
||||
let layout = build(&schema, &vs);
|
||||
assert_eq!(
|
||||
layout.get("payload.__discriminator"),
|
||||
Some(&FieldPosition { offset: 0, size: 1, kind: "TypeDef:Uint8".into() })
|
||||
Some(&FieldPosition {
|
||||
offset: 0,
|
||||
size: 1,
|
||||
kind: "TypeDef:Uint8".into()
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
layout.get("payload.data"),
|
||||
Some(&FieldPosition { offset: 9, size: 4, kind: "TypeDef:Uint32".into() })
|
||||
Some(&FieldPosition {
|
||||
offset: 9,
|
||||
size: 4,
|
||||
kind: "TypeDef:Uint32".into()
|
||||
})
|
||||
);
|
||||
assert_eq!(layout.total_size(), 13);
|
||||
}
|
||||
@@ -1111,22 +1258,31 @@ mod tests {
|
||||
}
|
||||
}
|
||||
});
|
||||
let vs = var_sizes(&[
|
||||
("packet.__discriminator", 5),
|
||||
("packet.path", 8),
|
||||
]);
|
||||
let vs = var_sizes(&[("packet.__discriminator", 5), ("packet.path", 8)]);
|
||||
let layout = build(&schema, &vs);
|
||||
assert_eq!(
|
||||
layout.get("packet.__discriminator"),
|
||||
Some(&FieldPosition { offset: 0, size: 1, kind: "TypeDef:Uint8".into() })
|
||||
Some(&FieldPosition {
|
||||
offset: 0,
|
||||
size: 1,
|
||||
kind: "TypeDef:Uint8".into()
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
layout.get("packet.handle"),
|
||||
Some(&FieldPosition { offset: 1, size: 4, kind: "TypeDef:Uint32".into() })
|
||||
Some(&FieldPosition {
|
||||
offset: 1,
|
||||
size: 4,
|
||||
kind: "TypeDef:Uint32".into()
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
layout.get("packet.path"),
|
||||
Some(&FieldPosition { offset: 5, size: 4, kind: "TypeDef:String".into() })
|
||||
Some(&FieldPosition {
|
||||
offset: 5,
|
||||
size: 4,
|
||||
kind: "TypeDef:String".into()
|
||||
})
|
||||
);
|
||||
assert_eq!(layout.total_size(), 17);
|
||||
}
|
||||
@@ -1231,11 +1387,19 @@ mod tests {
|
||||
let layout = build(&schema, &vs);
|
||||
assert_eq!(
|
||||
layout.get("event.type"),
|
||||
Some(&FieldPosition { offset: 0, size: 1, kind: "TypeDef:Uint8".into() })
|
||||
Some(&FieldPosition {
|
||||
offset: 0,
|
||||
size: 1,
|
||||
kind: "TypeDef:Uint8".into()
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
layout.get("event.handle"),
|
||||
Some(&FieldPosition { offset: 1, size: 4, kind: "TypeDef:Uint32".into() })
|
||||
Some(&FieldPosition {
|
||||
offset: 1,
|
||||
size: 4,
|
||||
kind: "TypeDef:Uint32".into()
|
||||
})
|
||||
);
|
||||
assert_eq!(layout.total_size(), 5);
|
||||
}
|
||||
@@ -1276,7 +1440,11 @@ mod tests {
|
||||
let layout = build(&schema, &vs);
|
||||
assert_eq!(
|
||||
layout.get("event.length"),
|
||||
Some(&FieldPosition { offset: 5, size: 4, kind: "TypeDef:Uint32".into() })
|
||||
Some(&FieldPosition {
|
||||
offset: 5,
|
||||
size: 4,
|
||||
kind: "TypeDef:Uint32".into()
|
||||
})
|
||||
);
|
||||
assert_eq!(layout.total_size(), 9);
|
||||
}
|
||||
@@ -1450,7 +1618,11 @@ mod tests {
|
||||
let layout = build(&schema, &var_sizes(&[("name", 5)]));
|
||||
assert_eq!(
|
||||
layout.get("name"),
|
||||
Some(&FieldPosition { offset: 0, size: 4, kind: "TypeDef:String".into() })
|
||||
Some(&FieldPosition {
|
||||
offset: 0,
|
||||
size: 4,
|
||||
kind: "TypeDef:String".into()
|
||||
})
|
||||
);
|
||||
assert_eq!(layout.total_size(), 9);
|
||||
}
|
||||
@@ -1524,11 +1696,19 @@ mod tests {
|
||||
let layout = build(&schema, &vs);
|
||||
assert_eq!(
|
||||
layout.get("packet.__discriminator"),
|
||||
Some(&FieldPosition { offset: 0, size: 1, kind: "TypeDef:Uint8".into() })
|
||||
Some(&FieldPosition {
|
||||
offset: 0,
|
||||
size: 1,
|
||||
kind: "TypeDef:Uint8".into()
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
layout.get("packet.x"),
|
||||
Some(&FieldPosition { offset: 1, size: 4, kind: "TypeDef:Uint32".into() })
|
||||
Some(&FieldPosition {
|
||||
offset: 1,
|
||||
size: 4,
|
||||
kind: "TypeDef:Uint32".into()
|
||||
})
|
||||
);
|
||||
assert_eq!(layout.total_size(), 5);
|
||||
}
|
||||
@@ -1564,16 +1744,28 @@ mod tests {
|
||||
let layout = build(&schema, &vs);
|
||||
assert_eq!(
|
||||
layout.get("id"),
|
||||
Some(&FieldPosition { offset: 0, size: 1, kind: "TypeDef:Uint8".into() })
|
||||
Some(&FieldPosition {
|
||||
offset: 0,
|
||||
size: 1,
|
||||
kind: "TypeDef:Uint8".into()
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
layout.get("payload.__discriminator"),
|
||||
Some(&FieldPosition { offset: 1, size: 1, kind: "TypeDef:Uint8".into() })
|
||||
Some(&FieldPosition {
|
||||
offset: 1,
|
||||
size: 1,
|
||||
kind: "TypeDef:Uint8".into()
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
layout.get("payload.handle"),
|
||||
Some(&FieldPosition { offset: 2, size: 4, kind: "TypeDef:Uint32".into() })
|
||||
Some(&FieldPosition {
|
||||
offset: 2,
|
||||
size: 4,
|
||||
kind: "TypeDef:Uint32".into()
|
||||
})
|
||||
);
|
||||
assert_eq!(layout.total_size(), 6);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,14 +31,14 @@ pub mod sequential_reader;
|
||||
pub mod tunion;
|
||||
pub mod validation;
|
||||
|
||||
pub use engine::{LayoutMode, TypedefEngine};
|
||||
pub use error::TypedefError;
|
||||
pub use schema::{
|
||||
parse_align, parse_discriminator, parse_encoding, parse_endian, parse_max_length,
|
||||
normalize_refs, DiscriminatorKind, Endian, VariableEncoding,
|
||||
};
|
||||
pub use validation::build_validator;
|
||||
pub use offset_map::{ByteRange, OffsetMap};
|
||||
pub use layout_builder::{FieldPosition, LayoutBuilder, PackedLayout};
|
||||
pub use offset_map::{ByteRange, OffsetMap};
|
||||
pub use schema::{
|
||||
normalize_refs, parse_align, parse_discriminator, parse_encoding, parse_endian,
|
||||
parse_max_length, DiscriminatorKind, Endian, VariableEncoding,
|
||||
};
|
||||
pub use sequential_reader::{FieldValue, SequentialReader};
|
||||
pub use tunion::UnionDispatch;
|
||||
pub use engine::{LayoutMode, TypedefEngine};
|
||||
pub use validation::build_validator;
|
||||
@@ -165,9 +165,9 @@ impl<'a> ComputeCtx<'a> {
|
||||
prefix: &str,
|
||||
parent_struct_align: usize,
|
||||
) -> Result<(usize, usize), TypedefError> {
|
||||
let obj = struct_schema.as_object().ok_or_else(|| {
|
||||
TypedefError::Schema("struct schema is not an object".to_string())
|
||||
})?;
|
||||
let obj = struct_schema
|
||||
.as_object()
|
||||
.ok_or_else(|| TypedefError::Schema("struct schema is not an object".to_string()))?;
|
||||
let properties = obj
|
||||
.get("properties")
|
||||
.and_then(|v| v.as_object())
|
||||
@@ -316,7 +316,10 @@ impl<'a> ComputeCtx<'a> {
|
||||
let union_default_align = union_align_annotation.unwrap_or(struct_default_align);
|
||||
|
||||
match disc {
|
||||
DiscriminatorKind::Byte { offset: disc_off, disc_type } => {
|
||||
DiscriminatorKind::Byte {
|
||||
offset: disc_off,
|
||||
disc_type,
|
||||
} => {
|
||||
let disc_size = type_size(&disc_type).ok_or_else(|| TypedefError::Offset {
|
||||
field_path: field_path.to_string(),
|
||||
reason: format!("discriminator type {disc_type} has no fixed size"),
|
||||
@@ -451,21 +454,22 @@ impl<'a> ComputeCtx<'a> {
|
||||
field_path: &str,
|
||||
struct_default_align: usize,
|
||||
) -> Result<FieldLayout, TypedefError> {
|
||||
let obj = field_schema.as_object().ok_or_else(|| TypedefError::Offset {
|
||||
field_path: field_path.to_string(),
|
||||
reason: "array schema is not an object".to_string(),
|
||||
})?;
|
||||
let obj = field_schema
|
||||
.as_object()
|
||||
.ok_or_else(|| TypedefError::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 {
|
||||
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 {
|
||||
let element_schema =
|
||||
resolve_ref_or_inline(items, self.root).ok_or_else(|| TypedefError::Offset {
|
||||
field_path: field_path.to_string(),
|
||||
reason: "could not resolve TArray items schema".to_string(),
|
||||
}
|
||||
})?;
|
||||
})?;
|
||||
let elem_kind = get_typedef_kind(element_schema).ok_or_else(|| TypedefError::Offset {
|
||||
field_path: field_path.to_string(),
|
||||
reason: "TArray element schema has no TypeDef:* kind".to_string(),
|
||||
@@ -487,8 +491,14 @@ impl<'a> ComputeCtx<'a> {
|
||||
let elem_align = field_alignment(element_schema, struct_default_align, elem_natural);
|
||||
let stride = round_up(elem_size, elem_align);
|
||||
|
||||
let min_items = obj.get("minItems").and_then(|v| v.as_u64()).map(|n| n as usize);
|
||||
let max_items = obj.get("maxItems").and_then(|v| v.as_u64()).map(|n| n as usize);
|
||||
let min_items = obj
|
||||
.get("minItems")
|
||||
.and_then(|v| v.as_u64())
|
||||
.map(|n| n as usize);
|
||||
let max_items = obj
|
||||
.get("maxItems")
|
||||
.and_then(|v| v.as_u64())
|
||||
.map(|n| n as usize);
|
||||
let fixed_count = match (min_items, max_items) {
|
||||
(Some(mn), Some(mx)) if mn == mx => Some(mn),
|
||||
_ => None,
|
||||
@@ -718,7 +728,10 @@ mod tests {
|
||||
});
|
||||
let m = map(&schema);
|
||||
assert_eq!(m.get("header.magic"), Some(&ByteRange { start: 0, end: 4 }));
|
||||
assert_eq!(m.get("header.version"), Some(&ByteRange { start: 4, end: 5 }));
|
||||
assert_eq!(
|
||||
m.get("header.version"),
|
||||
Some(&ByteRange { start: 4, end: 5 })
|
||||
);
|
||||
assert_eq!(m.get("body"), Some(&ByteRange { start: 8, end: 12 }));
|
||||
assert_eq!(m.total_size(), 12);
|
||||
}
|
||||
@@ -881,10 +894,7 @@ mod tests {
|
||||
}
|
||||
});
|
||||
let m = map(&schema);
|
||||
assert_eq!(
|
||||
m.get("event.type"),
|
||||
Some(&ByteRange { start: 0, end: 1 })
|
||||
);
|
||||
assert_eq!(m.get("event.type"), Some(&ByteRange { start: 0, end: 1 }));
|
||||
assert_eq!(m.total_size(), 12);
|
||||
}
|
||||
|
||||
@@ -952,7 +962,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn compute_rejects_non_struct_top_level() {
|
||||
let schema = json!({ "TypeDef:Union": true, "discriminator": { "kind": "byte" }, "mapping": {} });
|
||||
let schema =
|
||||
json!({ "TypeDef:Union": true, "discriminator": { "kind": "byte" }, "mapping": {} });
|
||||
let err = OffsetMap::compute(&schema).unwrap_err();
|
||||
assert!(matches!(err, TypedefError::Schema(_)));
|
||||
}
|
||||
@@ -973,4 +984,4 @@ mod tests {
|
||||
assert_eq!(empty.len(), 0);
|
||||
assert!(empty.is_empty());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,11 +13,7 @@ use serde_json::Value;
|
||||
|
||||
const TYPEDEF_PREFIX: &str = "TypeDef:";
|
||||
|
||||
const BYTE_DISCRIMINATOR_TYPES: &[&str] = &[
|
||||
"TypeDef:Uint8",
|
||||
"TypeDef:Uint16",
|
||||
"TypeDef:Uint32",
|
||||
];
|
||||
const BYTE_DISCRIMINATOR_TYPES: &[&str] = &["TypeDef:Uint8", "TypeDef:Uint16", "TypeDef:Uint32"];
|
||||
|
||||
/// Returns the `TypeDef:*` kind string if the schema node declares one.
|
||||
/// Returns `None` if the node has no `TypeDef:*` keyword.
|
||||
@@ -48,13 +44,8 @@ pub fn type_size(kind: &str) -> Option<usize> {
|
||||
"TypeDef:Float64" => Some(8),
|
||||
"TypeDef:Int8" | "TypeDef:Uint8" | "TypeDef:Boolean" => Some(1),
|
||||
"TypeDef:Int16" | "TypeDef:Uint16" => Some(2),
|
||||
"TypeDef:String"
|
||||
| "TypeDef:Bytes"
|
||||
| "TypeDef:Struct"
|
||||
| "TypeDef:Union"
|
||||
| "TypeDef:Array"
|
||||
| "TypeDef:Record"
|
||||
| "TypeDef:Timestamp" => None,
|
||||
"TypeDef:String" | "TypeDef:Bytes" | "TypeDef:Struct" | "TypeDef:Union"
|
||||
| "TypeDef:Array" | "TypeDef:Record" | "TypeDef:Timestamp" => None,
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -157,7 +148,11 @@ pub fn parse_max_length(node: &Value) -> Option<usize> {
|
||||
/// Parse the `"endian"` annotation. Defaults to `Little` if absent or
|
||||
/// unrecognized. Operates on any node, not just the root.
|
||||
pub fn parse_endian(node: &Value) -> Endian {
|
||||
match node.as_object().and_then(|o| o.get("endian")).and_then(Value::as_str) {
|
||||
match node
|
||||
.as_object()
|
||||
.and_then(|o| o.get("endian"))
|
||||
.and_then(Value::as_str)
|
||||
{
|
||||
Some("big") => Endian::Big,
|
||||
_ => Endian::Little,
|
||||
}
|
||||
@@ -196,18 +191,16 @@ pub fn parse_discriminator(node: &Value) -> Result<DiscriminatorKind, TypedefErr
|
||||
let disc = obj.get("discriminator").ok_or_else(|| {
|
||||
TypedefError::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())
|
||||
})?;
|
||||
let kind = disc_obj.get("kind").and_then(Value::as_str).ok_or_else(|| {
|
||||
TypedefError::Schema("discriminator is missing 'kind' field".to_string())
|
||||
})?;
|
||||
let disc_obj = disc
|
||||
.as_object()
|
||||
.ok_or_else(|| TypedefError::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()))?;
|
||||
match kind {
|
||||
"byte" => {
|
||||
let offset = disc_obj
|
||||
.get("offset")
|
||||
.and_then(Value::as_u64)
|
||||
.unwrap_or(0) as usize;
|
||||
let offset = disc_obj.get("offset").and_then(Value::as_u64).unwrap_or(0) as usize;
|
||||
let disc_type = disc_obj
|
||||
.get("type")
|
||||
.and_then(Value::as_str)
|
||||
@@ -377,8 +370,14 @@ mod tests {
|
||||
#[test]
|
||||
fn endian_from_schema_defaults_to_little() {
|
||||
assert_eq!(Endian::from_schema(&json!({})), Endian::Little);
|
||||
assert_eq!(Endian::from_schema(&json!({"endian": "little"})), Endian::Little);
|
||||
assert_eq!(Endian::from_schema(&json!({"endian": "weird"})), Endian::Little);
|
||||
assert_eq!(
|
||||
Endian::from_schema(&json!({"endian": "little"})),
|
||||
Endian::Little
|
||||
);
|
||||
assert_eq!(
|
||||
Endian::from_schema(&json!({"endian": "weird"})),
|
||||
Endian::Little
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -388,7 +387,10 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn parse_encoding_shorthand_true() {
|
||||
assert_eq!(parse_encoding(&json!(true)), VariableEncoding::LengthPrefixed);
|
||||
assert_eq!(
|
||||
parse_encoding(&json!(true)),
|
||||
VariableEncoding::LengthPrefixed
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -414,7 +416,10 @@ mod tests {
|
||||
VariableEncoding::LengthPrefixed
|
||||
);
|
||||
assert_eq!(parse_encoding(&json!(42)), VariableEncoding::LengthPrefixed);
|
||||
assert_eq!(parse_encoding(&json!(null)), VariableEncoding::LengthPrefixed);
|
||||
assert_eq!(
|
||||
parse_encoding(&json!(null)),
|
||||
VariableEncoding::LengthPrefixed
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -450,10 +455,13 @@ mod tests {
|
||||
fn parse_discriminator_byte_default_offset_and_type() {
|
||||
let schema = json!({"discriminator": {"kind": "byte"}});
|
||||
let disc = parse_discriminator(&schema).expect("byte discriminator");
|
||||
assert_eq!(disc, DiscriminatorKind::Byte {
|
||||
offset: 0,
|
||||
disc_type: "TypeDef:Uint8".to_string(),
|
||||
});
|
||||
assert_eq!(
|
||||
disc,
|
||||
DiscriminatorKind::Byte {
|
||||
offset: 0,
|
||||
disc_type: "TypeDef:Uint8".to_string(),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -462,19 +470,25 @@ mod tests {
|
||||
"discriminator": {"kind": "byte", "offset": 4, "type": "TypeDef:Uint16"}
|
||||
});
|
||||
let disc = parse_discriminator(&schema).expect("byte discriminator");
|
||||
assert_eq!(disc, DiscriminatorKind::Byte {
|
||||
offset: 4,
|
||||
disc_type: "TypeDef:Uint16".to_string(),
|
||||
});
|
||||
assert_eq!(
|
||||
disc,
|
||||
DiscriminatorKind::Byte {
|
||||
offset: 4,
|
||||
disc_type: "TypeDef:Uint16".to_string(),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_discriminator_field() {
|
||||
let schema = json!({"discriminator": {"kind": "field", "name": "type"}});
|
||||
let disc = parse_discriminator(&schema).expect("field discriminator");
|
||||
assert_eq!(disc, DiscriminatorKind::Field {
|
||||
name: "type".to_string()
|
||||
});
|
||||
assert_eq!(
|
||||
disc,
|
||||
DiscriminatorKind::Field {
|
||||
name: "type".to_string()
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -580,4 +594,4 @@ mod tests {
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -116,9 +116,8 @@ impl SequentialReader {
|
||||
/// 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<Self, TypedefError> {
|
||||
let kind = schema::get_typedef_kind(schema).ok_or_else(|| {
|
||||
TypedefError::Schema("schema has no TypeDef:* kind".to_string())
|
||||
})?;
|
||||
let kind = schema::get_typedef_kind(schema)
|
||||
.ok_or_else(|| TypedefError::Schema("schema has no TypeDef:* kind".to_string()))?;
|
||||
if kind != "TypeDef:Struct" {
|
||||
return Err(TypedefError::Schema(format!(
|
||||
"SequentialReader only supports TypeDef:Struct at the top level, got {kind}"
|
||||
@@ -143,7 +142,7 @@ impl SequentialReader {
|
||||
field_index: 0,
|
||||
position: 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the next field from `buffer` at the current position.
|
||||
///
|
||||
@@ -407,20 +406,22 @@ fn read_union_value<'a>(
|
||||
.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(|| TypedefError::Schema(format!("union {field_path} has no mapping object")))?;
|
||||
|
||||
match disc {
|
||||
DiscriminatorKind::Byte { offset: disc_offset, disc_type } => {
|
||||
let abs_offset = offset
|
||||
.checked_add(disc_offset)
|
||||
.ok_or_else(|| TypedefError::Access {
|
||||
field_path: field_path.to_string(),
|
||||
reason: format!(
|
||||
"discriminator offset {offset} + {disc_offset} overflows usize"
|
||||
),
|
||||
})?;
|
||||
DiscriminatorKind::Byte {
|
||||
offset: disc_offset,
|
||||
disc_type,
|
||||
} => {
|
||||
let abs_offset =
|
||||
offset
|
||||
.checked_add(disc_offset)
|
||||
.ok_or_else(|| TypedefError::Access {
|
||||
field_path: field_path.to_string(),
|
||||
reason: format!(
|
||||
"discriminator offset {offset} + {disc_offset} overflows usize"
|
||||
),
|
||||
})?;
|
||||
let (disc_value, disc_size) =
|
||||
read_byte_discriminator(buffer, abs_offset, field_path, &disc_type, endian)?;
|
||||
let key = disc_value.to_string();
|
||||
@@ -428,14 +429,13 @@ fn read_union_value<'a>(
|
||||
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 {
|
||||
field_path: field_path.to_string(),
|
||||
reason: format!(
|
||||
"variant start {abs_offset} + {disc_size} overflows usize"
|
||||
),
|
||||
})?;
|
||||
let variant_start =
|
||||
abs_offset
|
||||
.checked_add(disc_size)
|
||||
.ok_or_else(|| TypedefError::Access {
|
||||
field_path: field_path.to_string(),
|
||||
reason: format!("variant start {abs_offset} + {disc_size} overflows usize"),
|
||||
})?;
|
||||
let variant_size = resolve_and_walk_variant(
|
||||
root_schema,
|
||||
schema,
|
||||
@@ -445,14 +445,15 @@ fn read_union_value<'a>(
|
||||
endian,
|
||||
field_path,
|
||||
)?;
|
||||
let end = variant_start
|
||||
.checked_add(variant_size)
|
||||
.ok_or_else(|| TypedefError::Access {
|
||||
field_path: field_path.to_string(),
|
||||
reason: format!(
|
||||
"union end {variant_start} + {variant_size} overflows usize"
|
||||
),
|
||||
})?;
|
||||
let end =
|
||||
variant_start
|
||||
.checked_add(variant_size)
|
||||
.ok_or_else(|| TypedefError::Access {
|
||||
field_path: field_path.to_string(),
|
||||
reason: format!(
|
||||
"union end {variant_start} + {variant_size} overflows usize"
|
||||
),
|
||||
})?;
|
||||
Ok((
|
||||
FieldValue::Union {
|
||||
discriminator: key,
|
||||
@@ -476,14 +477,8 @@ fn read_union_value<'a>(
|
||||
"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 (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 {
|
||||
field_path: field_path.to_string(),
|
||||
@@ -502,9 +497,7 @@ fn read_union_value<'a>(
|
||||
.checked_add(variant_size)
|
||||
.ok_or_else(|| TypedefError::Access {
|
||||
field_path: field_path.to_string(),
|
||||
reason: format!(
|
||||
"union end {after_disc} + {variant_size} overflows usize"
|
||||
),
|
||||
reason: format!("union end {after_disc} + {variant_size} overflows usize"),
|
||||
})?;
|
||||
Ok((
|
||||
FieldValue::Union {
|
||||
@@ -548,7 +541,10 @@ fn read_byte_discriminator(
|
||||
/// Stringify a field-name discriminator value. Only the common kinds
|
||||
/// (String, Uint8/16/32, Enum) are supported — anything else is a schema
|
||||
/// error.
|
||||
fn discriminator_string_value(value: &FieldValue<'_>, field_path: &str) -> Result<String, TypedefError> {
|
||||
fn discriminator_string_value(
|
||||
value: &FieldValue<'_>,
|
||||
field_path: &str,
|
||||
) -> Result<String, TypedefError> {
|
||||
match value {
|
||||
FieldValue::String(s) => Ok(s.to_string()),
|
||||
FieldValue::U8(v) => Ok(v.to_string()),
|
||||
@@ -577,12 +573,18 @@ fn read_array_value<'a>(
|
||||
let obj = schema.as_object().ok_or_else(|| {
|
||||
TypedefError::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"))
|
||||
})?;
|
||||
let items_schema = obj
|
||||
.get("items")
|
||||
.ok_or_else(|| TypedefError::Schema(format!("array {field_path} has no items schema")))?;
|
||||
|
||||
let min = obj.get("minItems").and_then(Value::as_u64).map(|n| n as u32);
|
||||
let max = obj.get("maxItems").and_then(Value::as_u64).map(|n| n as u32);
|
||||
let min = obj
|
||||
.get("minItems")
|
||||
.and_then(Value::as_u64)
|
||||
.map(|n| n as u32);
|
||||
let max = obj
|
||||
.get("maxItems")
|
||||
.and_then(Value::as_u64)
|
||||
.map(|n| n as u32);
|
||||
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(|| {
|
||||
@@ -623,9 +625,7 @@ fn read_array_value<'a>(
|
||||
.checked_mul(element_stride)
|
||||
.ok_or_else(|| TypedefError::Access {
|
||||
field_path: field_path.to_string(),
|
||||
reason: format!(
|
||||
"array size {count} × stride {element_stride} overflows usize"
|
||||
),
|
||||
reason: format!("array size {count} × stride {element_stride} overflows usize"),
|
||||
})?
|
||||
};
|
||||
|
||||
@@ -672,9 +672,7 @@ fn walk_variable_array_size(
|
||||
if new_position < position {
|
||||
return Err(TypedefError::Access {
|
||||
field_path: element_path,
|
||||
reason: format!(
|
||||
"array element walked backwards: {position} → {new_position}"
|
||||
),
|
||||
reason: format!("array element walked backwards: {position} → {new_position}"),
|
||||
});
|
||||
}
|
||||
position = new_position;
|
||||
@@ -698,9 +696,7 @@ fn read_record_value<'a>(
|
||||
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(|| TypedefError::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");
|
||||
@@ -733,11 +729,11 @@ fn resolve_and_walk_variant(
|
||||
endian: Endian,
|
||||
field_path: &str,
|
||||
) -> Result<usize, TypedefError> {
|
||||
let resolved = resolve_variant_schema(root_schema, union_schema, variant_schema)
|
||||
.ok_or_else(|| {
|
||||
let resolved =
|
||||
resolve_variant_schema(root_schema, union_schema, variant_schema).ok_or_else(|| {
|
||||
TypedefError::Schema(format!(
|
||||
"union {field_path} variant could not be resolved: {variant_schema}"
|
||||
))
|
||||
"union {field_path} variant could not be resolved: {variant_schema}"
|
||||
))
|
||||
})?;
|
||||
let kind = schema::get_typedef_kind(resolved).ok_or_else(|| {
|
||||
TypedefError::Schema(format!(
|
||||
@@ -745,9 +741,7 @@ fn resolve_and_walk_variant(
|
||||
))
|
||||
})?;
|
||||
match kind {
|
||||
"TypeDef:Struct" => {
|
||||
walk_struct_size(root_schema, resolved, buffer, variant_start, endian)
|
||||
}
|
||||
"TypeDef:Struct" => walk_struct_size(root_schema, resolved, buffer, variant_start, endian),
|
||||
"TypeDef:Union" => {
|
||||
let (_, end) = read_union_value(
|
||||
buffer,
|
||||
@@ -834,9 +828,7 @@ fn walk_struct_size(
|
||||
if new_position < position {
|
||||
return Err(TypedefError::Access {
|
||||
field_path: name.clone(),
|
||||
reason: format!(
|
||||
"struct field walked backwards: {position} → {new_position}"
|
||||
),
|
||||
reason: format!("struct field walked backwards: {position} → {new_position}"),
|
||||
});
|
||||
}
|
||||
position = new_position;
|
||||
@@ -1150,7 +1142,8 @@ mod tests {
|
||||
assert_eq!(end, 3);
|
||||
let inner_schema = &nested["properties"]["inner"];
|
||||
let inner_reader = SequentialReader::new(inner_schema).unwrap();
|
||||
let inner_end = walk_struct_size(inner_schema, inner_schema, &buf, start, LE).unwrap();
|
||||
let inner_end =
|
||||
walk_struct_size(inner_schema, inner_schema, &buf, start, LE).unwrap();
|
||||
assert_eq!(inner_end, end - start);
|
||||
let _ = inner_reader;
|
||||
}
|
||||
@@ -1191,7 +1184,10 @@ mod tests {
|
||||
let (name, value) = reader.read_next(&buf).unwrap().unwrap();
|
||||
assert_eq!(name, "packet");
|
||||
match value {
|
||||
FieldValue::Union { discriminator, variant_start } => {
|
||||
FieldValue::Union {
|
||||
discriminator,
|
||||
variant_start,
|
||||
} => {
|
||||
assert_eq!(discriminator, "5");
|
||||
assert_eq!(variant_start, 1);
|
||||
}
|
||||
@@ -1230,7 +1226,10 @@ mod tests {
|
||||
let (name, value) = reader.read_next(&buf).unwrap().unwrap();
|
||||
assert_eq!(name, "event");
|
||||
match value {
|
||||
FieldValue::Union { discriminator, variant_start } => {
|
||||
FieldValue::Union {
|
||||
discriminator,
|
||||
variant_start,
|
||||
} => {
|
||||
assert_eq!(discriminator, "read");
|
||||
assert_eq!(variant_start, after);
|
||||
}
|
||||
@@ -1258,7 +1257,11 @@ mod tests {
|
||||
let (name, value) = reader.read_next(&buf).unwrap().unwrap();
|
||||
assert_eq!(name, "vals");
|
||||
match value {
|
||||
FieldValue::Array { count, element_start, element_stride } => {
|
||||
FieldValue::Array {
|
||||
count,
|
||||
element_start,
|
||||
element_stride,
|
||||
} => {
|
||||
assert_eq!(count, 3);
|
||||
assert_eq!(element_start, 0);
|
||||
assert_eq!(element_stride, 1);
|
||||
@@ -1288,7 +1291,11 @@ mod tests {
|
||||
let (name, value) = reader.read_next(&buf).unwrap().unwrap();
|
||||
assert_eq!(name, "vals");
|
||||
match value {
|
||||
FieldValue::Array { count, element_start, element_stride } => {
|
||||
FieldValue::Array {
|
||||
count,
|
||||
element_start,
|
||||
element_stride,
|
||||
} => {
|
||||
assert_eq!(count, 2);
|
||||
assert_eq!(element_start, 4);
|
||||
assert_eq!(element_stride, 2);
|
||||
@@ -1319,7 +1326,11 @@ mod tests {
|
||||
let (name, value) = reader.read_next(&buf).unwrap().unwrap();
|
||||
assert_eq!(name, "items");
|
||||
match value {
|
||||
FieldValue::Array { count, element_start, element_stride } => {
|
||||
FieldValue::Array {
|
||||
count,
|
||||
element_start,
|
||||
element_stride,
|
||||
} => {
|
||||
assert_eq!(count, 2);
|
||||
assert_eq!(element_start, 4);
|
||||
assert_eq!(element_stride, 0);
|
||||
@@ -1423,7 +1434,10 @@ mod tests {
|
||||
let (name, value) = reader.read_next(&buf).unwrap().unwrap();
|
||||
assert_eq!(name, "packet");
|
||||
match value {
|
||||
FieldValue::Union { discriminator, variant_start } => {
|
||||
FieldValue::Union {
|
||||
discriminator,
|
||||
variant_start,
|
||||
} => {
|
||||
assert_eq!(discriminator, "5");
|
||||
assert_eq!(variant_start, 1);
|
||||
}
|
||||
@@ -1457,7 +1471,10 @@ mod tests {
|
||||
let (name, value) = reader.read_next(&buf).unwrap().unwrap();
|
||||
assert_eq!(name, "packet");
|
||||
match value {
|
||||
FieldValue::Union { discriminator, variant_start } => {
|
||||
FieldValue::Union {
|
||||
discriminator,
|
||||
variant_start,
|
||||
} => {
|
||||
assert_eq!(discriminator, "5");
|
||||
assert_eq!(variant_start, 1);
|
||||
}
|
||||
@@ -1517,4 +1534,4 @@ mod tests {
|
||||
assert_eq!(value, FieldValue::U8(7));
|
||||
assert_eq!(reader.position(), expected_end);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -79,14 +79,15 @@ pub fn read_byte_discriminator(
|
||||
let key = disc_value.to_string();
|
||||
verify_mapping_key(union_schema, &key, DISCRIMINATOR_PATH, &key)?;
|
||||
|
||||
let variant_offset = offset
|
||||
.checked_add(discriminator_size)
|
||||
.ok_or_else(|| TypedefError::Access {
|
||||
field_path: DISCRIMINATOR_PATH.to_string(),
|
||||
reason: format!(
|
||||
"offset {offset} + discriminator_size {discriminator_size} overflows usize"
|
||||
),
|
||||
})?;
|
||||
let variant_offset =
|
||||
offset
|
||||
.checked_add(discriminator_size)
|
||||
.ok_or_else(|| TypedefError::Access {
|
||||
field_path: DISCRIMINATOR_PATH.to_string(),
|
||||
reason: format!(
|
||||
"offset {offset} + discriminator_size {discriminator_size} overflows usize"
|
||||
),
|
||||
})?;
|
||||
|
||||
Ok(UnionDispatch {
|
||||
key,
|
||||
@@ -149,15 +150,16 @@ pub fn read_field_discriminator(
|
||||
let (key, discriminator_field_size) = match kind {
|
||||
"TypeDef: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 {
|
||||
field_path: name.clone(),
|
||||
reason: format!(
|
||||
"string prefix {STRING_PREFIX_SIZE} + data length {} overflows usize",
|
||||
s.len()
|
||||
),
|
||||
})?;
|
||||
let size =
|
||||
STRING_PREFIX_SIZE
|
||||
.checked_add(s.len())
|
||||
.ok_or_else(|| TypedefError::Access {
|
||||
field_path: name.clone(),
|
||||
reason: format!(
|
||||
"string prefix {STRING_PREFIX_SIZE} + data length {} overflows usize",
|
||||
s.len()
|
||||
),
|
||||
})?;
|
||||
(s.to_string(), size)
|
||||
}
|
||||
"TypeDef:Uint8" => {
|
||||
@@ -208,10 +210,7 @@ pub fn read_field_discriminator(
|
||||
/// - [`TypedefError::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, TypedefError> {
|
||||
let mapping = union_schema
|
||||
.get("mapping")
|
||||
.and_then(Value::as_object)
|
||||
@@ -643,4 +642,4 @@ mod tests {
|
||||
let err = discriminator_size(&schema).unwrap_err();
|
||||
assert!(matches!(err, TypedefError::Schema(_)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -59,7 +59,9 @@ impl Keyword for Float32Validator {
|
||||
fn validate<'i>(&self, instance: &'i Value) -> Result<(), ValidationError<'i>> {
|
||||
match instance.as_f64() {
|
||||
Some(f) if f.is_finite() => Ok(()),
|
||||
_ => Err(ValidationError::custom("expected a finite f32-compatible number")),
|
||||
_ => Err(ValidationError::custom(
|
||||
"expected a finite f32-compatible number",
|
||||
)),
|
||||
}
|
||||
}
|
||||
fn is_valid(&self, instance: &Value) -> bool {
|
||||
@@ -85,7 +87,9 @@ impl Keyword for Int8Validator {
|
||||
fn validate<'i>(&self, instance: &'i Value) -> Result<(), ValidationError<'i>> {
|
||||
match instance.as_i64() {
|
||||
Some(n) if (-128..=127).contains(&n) => Ok(()),
|
||||
_ => Err(ValidationError::custom("expected an integer in range [-128, 127]")),
|
||||
_ => Err(ValidationError::custom(
|
||||
"expected an integer in range [-128, 127]",
|
||||
)),
|
||||
}
|
||||
}
|
||||
fn is_valid(&self, instance: &Value) -> bool {
|
||||
@@ -98,7 +102,9 @@ impl Keyword for Int16Validator {
|
||||
fn validate<'i>(&self, instance: &'i Value) -> Result<(), ValidationError<'i>> {
|
||||
match instance.as_i64() {
|
||||
Some(n) if (-32_768..=32_767).contains(&n) => Ok(()),
|
||||
_ => Err(ValidationError::custom("expected an integer in range [-32768, 32767]")),
|
||||
_ => Err(ValidationError::custom(
|
||||
"expected an integer in range [-32768, 32767]",
|
||||
)),
|
||||
}
|
||||
}
|
||||
fn is_valid(&self, instance: &Value) -> bool {
|
||||
@@ -130,7 +136,9 @@ impl Keyword for Uint8Validator {
|
||||
fn validate<'i>(&self, instance: &'i Value) -> Result<(), ValidationError<'i>> {
|
||||
match instance.as_u64() {
|
||||
Some(n) if n <= 255 => Ok(()),
|
||||
_ => Err(ValidationError::custom("expected an unsigned integer in range [0, 255]")),
|
||||
_ => Err(ValidationError::custom(
|
||||
"expected an unsigned integer in range [0, 255]",
|
||||
)),
|
||||
}
|
||||
}
|
||||
fn is_valid(&self, instance: &Value) -> bool {
|
||||
@@ -143,7 +151,9 @@ impl Keyword for Uint16Validator {
|
||||
fn validate<'i>(&self, instance: &'i Value) -> Result<(), ValidationError<'i>> {
|
||||
match instance.as_u64() {
|
||||
Some(n) if n <= 65_535 => Ok(()),
|
||||
_ => Err(ValidationError::custom("expected an unsigned integer in range [0, 65535]")),
|
||||
_ => Err(ValidationError::custom(
|
||||
"expected an unsigned integer in range [0, 65535]",
|
||||
)),
|
||||
}
|
||||
}
|
||||
fn is_valid(&self, instance: &Value) -> bool {
|
||||
@@ -191,9 +201,9 @@ impl Keyword for StringValidator {
|
||||
}
|
||||
}
|
||||
fn is_valid(&self, instance: &Value) -> bool {
|
||||
instance.as_str().is_some_and(|s| {
|
||||
self.max_length.is_none_or(|max| s.len() <= max)
|
||||
})
|
||||
instance
|
||||
.as_str()
|
||||
.is_some_and(|s| self.max_length.is_none_or(|max| s.len() <= max))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,9 +228,9 @@ impl Keyword for BytesValidator {
|
||||
}
|
||||
}
|
||||
fn is_valid(&self, instance: &Value) -> bool {
|
||||
instance.as_str().is_some_and(|s| {
|
||||
self.max_length.is_none_or(|max| s.len() <= max)
|
||||
})
|
||||
instance
|
||||
.as_str()
|
||||
.is_some_and(|s| self.max_length.is_none_or(|max| s.len() <= max))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,7 +252,9 @@ impl Keyword for TimestampValidator {
|
||||
fn validate<'i>(&self, instance: &'i Value) -> Result<(), ValidationError<'i>> {
|
||||
match instance.as_str() {
|
||||
Some(s) if is_rfc3339_timestamp(s) => Ok(()),
|
||||
_ => Err(ValidationError::custom("expected an RFC 3339 timestamp string")),
|
||||
_ => Err(ValidationError::custom(
|
||||
"expected an RFC 3339 timestamp string",
|
||||
)),
|
||||
}
|
||||
}
|
||||
fn is_valid(&self, instance: &Value) -> bool {
|
||||
@@ -277,11 +289,13 @@ fn is_rfc3339_timestamp(s: &str) -> bool {
|
||||
if time_parts.len() < 2 || time_parts.len() > 3 {
|
||||
return false;
|
||||
}
|
||||
date_parts[0]
|
||||
.parse::<u16>()
|
||||
.is_ok_and(|y| y > 0)
|
||||
&& date_parts[1].parse::<u8>().is_ok_and(|m| (1..=12).contains(&m))
|
||||
&& date_parts[2].parse::<u8>().is_ok_and(|d| (1..=31).contains(&d))
|
||||
date_parts[0].parse::<u16>().is_ok_and(|y| y > 0)
|
||||
&& date_parts[1]
|
||||
.parse::<u8>()
|
||||
.is_ok_and(|m| (1..=12).contains(&m))
|
||||
&& date_parts[2]
|
||||
.parse::<u8>()
|
||||
.is_ok_and(|d| (1..=31).contains(&d))
|
||||
&& time_parts[0].parse::<u8>().is_ok_and(|h| h <= 23)
|
||||
&& time_parts[1].parse::<u8>().is_ok_and(|m| m <= 59)
|
||||
}
|
||||
@@ -378,7 +392,9 @@ fn float32_factory<'a>(
|
||||
if value.as_bool() == Some(true) {
|
||||
Ok(Box::new(Float32Validator))
|
||||
} else {
|
||||
Err(ValidationError::schema("TypeDef:Float32 must be set to true"))
|
||||
Err(ValidationError::schema(
|
||||
"TypeDef:Float32 must be set to true",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -390,7 +406,9 @@ fn float64_factory<'a>(
|
||||
if value.as_bool() == Some(true) {
|
||||
Ok(Box::new(Float64Validator))
|
||||
} else {
|
||||
Err(ValidationError::schema("TypeDef:Float64 must be set to true"))
|
||||
Err(ValidationError::schema(
|
||||
"TypeDef:Float64 must be set to true",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -450,7 +468,9 @@ fn uint16_factory<'a>(
|
||||
if value.as_bool() == Some(true) {
|
||||
Ok(Box::new(Uint16Validator))
|
||||
} else {
|
||||
Err(ValidationError::schema("TypeDef:Uint16 must be set to true"))
|
||||
Err(ValidationError::schema(
|
||||
"TypeDef:Uint16 must be set to true",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -462,7 +482,9 @@ fn uint32_factory<'a>(
|
||||
if value.as_bool() == Some(true) {
|
||||
Ok(Box::new(Uint32Validator))
|
||||
} else {
|
||||
Err(ValidationError::schema("TypeDef:Uint32 must be set to true"))
|
||||
Err(ValidationError::schema(
|
||||
"TypeDef:Uint32 must be set to true",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -474,7 +496,9 @@ fn boolean_factory<'a>(
|
||||
if value.as_bool() == Some(true) {
|
||||
Ok(Box::new(BooleanValidator))
|
||||
} else {
|
||||
Err(ValidationError::schema("TypeDef:Boolean must be set to true"))
|
||||
Err(ValidationError::schema(
|
||||
"TypeDef:Boolean must be set to true",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -484,7 +508,9 @@ fn string_factory<'a>(
|
||||
_path: jsonschema::paths::Location,
|
||||
) -> Result<Box<dyn Keyword>, ValidationError<'a>> {
|
||||
if value.as_bool() != Some(true) {
|
||||
return Err(ValidationError::schema("TypeDef:String must be set to true"));
|
||||
return Err(ValidationError::schema(
|
||||
"TypeDef:String must be set to true",
|
||||
));
|
||||
}
|
||||
let max_length = parent
|
||||
.get("maxLength")
|
||||
@@ -528,7 +554,9 @@ fn struct_factory<'a>(
|
||||
if value.as_bool() == Some(true) {
|
||||
Ok(Box::new(StructValidator))
|
||||
} else {
|
||||
Err(ValidationError::schema("TypeDef:Struct must be set to true"))
|
||||
Err(ValidationError::schema(
|
||||
"TypeDef:Struct must be set to true",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -564,7 +592,9 @@ fn record_factory<'a>(
|
||||
if value.as_bool() == Some(true) {
|
||||
Ok(Box::new(RecordValidator))
|
||||
} else {
|
||||
Err(ValidationError::schema("TypeDef:Record must be set to true"))
|
||||
Err(ValidationError::schema(
|
||||
"TypeDef:Record must be set to true",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -576,7 +606,9 @@ fn timestamp_factory<'a>(
|
||||
if value.as_bool() == Some(true) {
|
||||
Ok(Box::new(TimestampValidator))
|
||||
} else {
|
||||
Err(ValidationError::schema("TypeDef:Timestamp must be set to true"))
|
||||
Err(ValidationError::schema(
|
||||
"TypeDef:Timestamp must be set to true",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -850,4 +882,4 @@ mod tests {
|
||||
let err = build_validator(&schema).expect_err("schema must be an object");
|
||||
assert!(matches!(err, TypedefError::Schema(_)), "got {err:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -201,8 +201,14 @@ fn read_write_aligned_round_trips_all_fixed_size_kinds() -> Result<(), TypedefEr
|
||||
assert_eq!(engine.read_field(&buffer, "u8")?, FieldValue::U8(0xAB));
|
||||
assert_eq!(engine.read_field(&buffer, "i16")?, FieldValue::I16(-32000));
|
||||
assert_eq!(engine.read_field(&buffer, "u16")?, FieldValue::U16(0xBEEF));
|
||||
assert_eq!(engine.read_field(&buffer, "i32")?, FieldValue::I32(-2_000_000_007));
|
||||
assert_eq!(engine.read_field(&buffer, "u32")?, FieldValue::U32(0xDEADBEEF));
|
||||
assert_eq!(
|
||||
engine.read_field(&buffer, "i32")?,
|
||||
FieldValue::I32(-2_000_000_007)
|
||||
);
|
||||
assert_eq!(
|
||||
engine.read_field(&buffer, "u32")?,
|
||||
FieldValue::U32(0xDEADBEEF)
|
||||
);
|
||||
assert_eq!(engine.read_field(&buffer, "f32")?, FieldValue::F32(1.5));
|
||||
assert_eq!(engine.read_field(&buffer, "f64")?, FieldValue::F64(2.5));
|
||||
assert_eq!(engine.read_field(&buffer, "b")?, FieldValue::Bool(true));
|
||||
@@ -368,4 +374,4 @@ fn read_field_aligned_reads_nested_struct_byte_range() -> Result<(), TypedefErro
|
||||
FieldValue::U32(0xCAFEBABE)
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -7,8 +7,8 @@
|
||||
//! variable-length field size in `LayoutBuilder::build`).
|
||||
|
||||
use alknet_typedef::data_access;
|
||||
use alknet_typedef::*;
|
||||
use alknet_typedef::tunion;
|
||||
use alknet_typedef::*;
|
||||
use serde_json::json;
|
||||
use std::collections::HashMap;
|
||||
|
||||
@@ -143,7 +143,8 @@ fn write_u32_buffer_too_short_returns_access_error() {
|
||||
#[test]
|
||||
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();
|
||||
let err =
|
||||
data_access::write_string(&mut buffer, 0, "hello", "name", Endian::Little).unwrap_err();
|
||||
assert!(matches!(err, TypedefError::Access { .. }), "got {err:?}");
|
||||
}
|
||||
|
||||
@@ -255,7 +256,8 @@ fn read_field_discriminator_unknown_value_returns_access_error() -> Result<(), T
|
||||
});
|
||||
let mut buffer = vec![0u8; 8];
|
||||
buffer[0] = 99;
|
||||
let err = tunion::read_field_discriminator(&buffer, &union_schema, 0, Endian::Little).unwrap_err();
|
||||
let err =
|
||||
tunion::read_field_discriminator(&buffer, &union_schema, 0, Endian::Little).unwrap_err();
|
||||
assert!(matches!(err, TypedefError::Access { .. }), "got {err:?}");
|
||||
Ok(())
|
||||
}
|
||||
@@ -274,7 +276,10 @@ fn layout_builder_missing_var_size_returns_offset_error() {
|
||||
match err {
|
||||
TypedefError::Offset { field_path, reason } => {
|
||||
assert_eq!(field_path, "name");
|
||||
assert!(reason.contains("missing variable-length field size"), "reason: {reason}");
|
||||
assert!(
|
||||
reason.contains("missing variable-length field size"),
|
||||
"reason: {reason}"
|
||||
);
|
||||
}
|
||||
other => panic!("expected Offset, got {other:?}"),
|
||||
}
|
||||
@@ -384,7 +389,8 @@ fn read_string_indirect_data_region_too_short_returns_access_error() {
|
||||
let _ = data_access::write_u32(&mut index, 0, 100, "idx.off", Endian::Little);
|
||||
let _ = data_access::write_u32(&mut index, 4, 10, "idx.len", Endian::Little);
|
||||
let data_region = b"too short";
|
||||
let err = data_access::read_bytes_indirect(&index, 0, data_region, "blob", Endian::Little).unwrap_err();
|
||||
let err = data_access::read_bytes_indirect(&index, 0, data_region, "blob", Endian::Little)
|
||||
.unwrap_err();
|
||||
assert!(matches!(err, TypedefError::Access { .. }), "got {err:?}");
|
||||
}
|
||||
|
||||
@@ -392,7 +398,8 @@ fn read_string_indirect_data_region_too_short_returns_access_error() {
|
||||
fn read_bytes_indirect_index_too_short_returns_access_error() {
|
||||
let buffer = [0u8; 4];
|
||||
let data_region = b"anything";
|
||||
let err = data_access::read_bytes_indirect(&buffer, 0, data_region, "blob", Endian::Little).unwrap_err();
|
||||
let err = data_access::read_bytes_indirect(&buffer, 0, data_region, "blob", Endian::Little)
|
||||
.unwrap_err();
|
||||
assert!(matches!(err, TypedefError::Access { .. }), "got {err:?}");
|
||||
}
|
||||
|
||||
@@ -402,6 +409,7 @@ fn read_string_indirect_invalid_utf8_returns_access_error() {
|
||||
let mut index = [0u8; 8];
|
||||
let _ = data_access::write_u32(&mut index, 0, 0, "idx.off", Endian::Little);
|
||||
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();
|
||||
let err = data_access::read_string_indirect(&index, 0, data_region, "name", Endian::Little)
|
||||
.unwrap_err();
|
||||
assert!(matches!(err, TypedefError::Access { .. }), "got {err:?}");
|
||||
}
|
||||
}
|
||||
@@ -8,17 +8,14 @@
|
||||
//! reads them back, asserting both the values and (where applicable)
|
||||
//! the byte positions.
|
||||
|
||||
use alknet_typedef::*;
|
||||
use alknet_typedef::data_access;
|
||||
use alknet_typedef::tunion;
|
||||
use alknet_typedef::*;
|
||||
use serde_json::json;
|
||||
use std::collections::HashMap;
|
||||
|
||||
fn var_sizes(pairs: &[(&str, usize)]) -> HashMap<String, usize> {
|
||||
pairs
|
||||
.iter()
|
||||
.map(|(k, v)| (k.to_string(), *v))
|
||||
.collect()
|
||||
pairs.iter().map(|(k, v)| (k.to_string(), *v)).collect()
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -42,7 +39,13 @@ fn fixed_size_round_trip_via_offset_map() -> Result<(), TypedefError> {
|
||||
let flag_range = offset_map.get("flag").expect("flag range");
|
||||
data_access::write_u8(&mut buffer, flag_range.start, 1, "flag")?;
|
||||
let count_range = offset_map.get("count").expect("count range");
|
||||
data_access::write_u16(&mut buffer, count_range.start, 1000, "count", Endian::Little)?;
|
||||
data_access::write_u16(
|
||||
&mut buffer,
|
||||
count_range.start,
|
||||
1000,
|
||||
"count",
|
||||
Endian::Little,
|
||||
)?;
|
||||
|
||||
assert_eq!(
|
||||
data_access::read_u32(&buffer, id_range.start, "id", Endian::Little)?,
|
||||
@@ -162,12 +165,35 @@ fn nested_struct_round_trip_via_offset_map() -> Result<(), TypedefError> {
|
||||
|
||||
let data = b"body-data".to_vec();
|
||||
let mut buffer = vec![0u8; offset_map.total_size() + data.len()];
|
||||
data_access::write_u32(&mut buffer, header_version.start, 1, "header.version", Endian::Little)?;
|
||||
data_access::write_u32(&mut buffer, header_magic.start, 0xCAFEBABE, "header.magic", Endian::Little)?;
|
||||
data_access::write_bytes(&mut buffer, payload_prefix.start, &data, "payload", Endian::Little)?;
|
||||
data_access::write_u32(
|
||||
&mut buffer,
|
||||
header_version.start,
|
||||
1,
|
||||
"header.version",
|
||||
Endian::Little,
|
||||
)?;
|
||||
data_access::write_u32(
|
||||
&mut buffer,
|
||||
header_magic.start,
|
||||
0xCAFEBABE,
|
||||
"header.magic",
|
||||
Endian::Little,
|
||||
)?;
|
||||
data_access::write_bytes(
|
||||
&mut buffer,
|
||||
payload_prefix.start,
|
||||
&data,
|
||||
"payload",
|
||||
Endian::Little,
|
||||
)?;
|
||||
|
||||
assert_eq!(
|
||||
data_access::read_u32(&buffer, header_version.start, "header.version", Endian::Little)?,
|
||||
data_access::read_u32(
|
||||
&buffer,
|
||||
header_version.start,
|
||||
"header.version",
|
||||
Endian::Little
|
||||
)?,
|
||||
1
|
||||
);
|
||||
assert_eq!(
|
||||
@@ -283,13 +309,22 @@ fn alignment_padding_round_trip_u8_then_u32() -> Result<(), TypedefError> {
|
||||
|
||||
let mut buffer = vec![0u8; offset_map.total_size()];
|
||||
data_access::write_u8(&mut buffer, flag_range.start, 0xAB, "flag")?;
|
||||
data_access::write_u32(&mut buffer, id_range.start, 0x01020304, "id", Endian::Little)?;
|
||||
data_access::write_u32(
|
||||
&mut buffer,
|
||||
id_range.start,
|
||||
0x01020304,
|
||||
"id",
|
||||
Endian::Little,
|
||||
)?;
|
||||
|
||||
assert_eq!(buffer[0], 0xAB);
|
||||
assert_eq!(&buffer[1..4], &[0x00, 0x00, 0x00]);
|
||||
assert_eq!(&buffer[4..8], 0x01020304u32.to_le_bytes());
|
||||
|
||||
assert_eq!(data_access::read_u8(&buffer, flag_range.start, "flag")?, 0xAB);
|
||||
assert_eq!(
|
||||
data_access::read_u8(&buffer, flag_range.start, "flag")?,
|
||||
0xAB
|
||||
);
|
||||
assert_eq!(
|
||||
data_access::read_u32(&buffer, id_range.start, "id", Endian::Little)?,
|
||||
0x01020304
|
||||
@@ -326,14 +361,23 @@ fn packed_layout_round_trip_via_layout_builder() -> Result<(), TypedefError> {
|
||||
let mut buffer = vec![0u8; layout.total_size()];
|
||||
data_access::write_u8(&mut buffer, flag_pos.offset, 0xAB, "flag")?;
|
||||
data_access::write_u32(&mut buffer, id_pos.offset, 0x01020304, "id", Endian::Little)?;
|
||||
data_access::write_string(&mut buffer, payload_pos.offset, payload_str, "payload", Endian::Little)?;
|
||||
data_access::write_string(
|
||||
&mut buffer,
|
||||
payload_pos.offset,
|
||||
payload_str,
|
||||
"payload",
|
||||
Endian::Little,
|
||||
)?;
|
||||
|
||||
assert_eq!(buffer[0], 0xAB);
|
||||
assert_eq!(&buffer[1..5], 0x01020304u32.to_le_bytes());
|
||||
assert_eq!(&buffer[5..9], 10u32.to_le_bytes());
|
||||
assert_eq!(&buffer[9..19], payload_bytes);
|
||||
|
||||
assert_eq!(data_access::read_u8(&buffer, flag_pos.offset, "flag")?, 0xAB);
|
||||
assert_eq!(
|
||||
data_access::read_u8(&buffer, flag_pos.offset, "flag")?,
|
||||
0xAB
|
||||
);
|
||||
assert_eq!(
|
||||
data_access::read_u32(&buffer, id_pos.offset, "id", Endian::Little)?,
|
||||
0x01020304
|
||||
@@ -459,7 +503,9 @@ fn tunion_byte_offset_discriminator_dispatch() -> Result<(), TypedefError> {
|
||||
|
||||
let variant = tunion::resolve_variant(&union_schema, &dispatch.key)?;
|
||||
assert_eq!(
|
||||
variant.get("TypeDef:Struct").and_then(serde_json::Value::as_bool),
|
||||
variant
|
||||
.get("TypeDef:Struct")
|
||||
.and_then(serde_json::Value::as_bool),
|
||||
Some(true)
|
||||
);
|
||||
Ok(())
|
||||
@@ -486,4 +532,4 @@ fn tunion_byte_offset_discriminator_size_lookup() -> Result<(), TypedefError> {
|
||||
assert_eq!(tunion::discriminator_size(&u16_schema)?, 2);
|
||||
assert_eq!(tunion::discriminator_size(&u32_schema)?, 4);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -58,7 +58,9 @@ 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").and_then(serde_json::Value::as_bool),
|
||||
variant
|
||||
.get("TypeDef:Struct")
|
||||
.and_then(serde_json::Value::as_bool),
|
||||
Some(true)
|
||||
);
|
||||
Ok(())
|
||||
@@ -177,7 +179,9 @@ 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").and_then(serde_json::Value::as_bool),
|
||||
variant
|
||||
.get("TypeDef:Struct")
|
||||
.and_then(serde_json::Value::as_bool),
|
||||
Some(true)
|
||||
);
|
||||
Ok(())
|
||||
@@ -263,7 +267,8 @@ fn read_field_discriminator_unknown_value_returns_access_error() -> Result<(), T
|
||||
});
|
||||
let mut buffer = vec![0u8; 8];
|
||||
buffer[0] = 99;
|
||||
let err = tunion::read_field_discriminator(&buffer, &union_schema, 0, Endian::Little).unwrap_err();
|
||||
let err =
|
||||
tunion::read_field_discriminator(&buffer, &union_schema, 0, Endian::Little).unwrap_err();
|
||||
assert!(matches!(err, TypedefError::Access { .. }), "got {err:?}");
|
||||
Ok(())
|
||||
}
|
||||
@@ -315,4 +320,4 @@ fn parse_discriminator_missing_returns_schema_error() {
|
||||
let schema = json!({"TypeDef:Union": true});
|
||||
let err = alknet_typedef::parse_discriminator(&schema).unwrap_err();
|
||||
assert!(matches!(err, TypedefError::Schema(_)), "got {err:?}");
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user