use crate::db::data::structural_field::cbor::{
parse_tagged_variant_payload_bytes, walk_cbor_array_items, walk_cbor_map_entries,
};
use crate::db::data::structural_field::value_storage::{
decode_untyped_enum_payload_bytes, normalize_map_entries_or_preserve,
validate_structural_value_storage_bytes, validate_untyped_enum_payload_bytes,
};
use crate::db::data::structural_field::{
FieldDecodeError, decode_structural_field_by_kind_bytes, decode_structural_value_storage_bytes,
validate_structural_field_by_kind_bytes,
};
use crate::{
model::field::{EnumVariantModel, FieldKind, FieldStorageDecode},
value::{Value, ValueEnum},
};
type KindArrayDecodeState = (Vec<Value>, FieldKind);
type KindMapDecodeState = (Vec<(Value, Value)>, FieldKind, FieldKind);
type KindArrayValidateState = FieldKind;
type KindMapValidateState = (FieldKind, FieldKind);
fn push_kind_array_item(item_bytes: &[u8], context: *mut ()) -> Result<(), FieldDecodeError> {
let state = unsafe { &mut *context.cast::<KindArrayDecodeState>() };
state
.0
.push(decode_structural_field_by_kind_bytes(item_bytes, state.1)?);
Ok(())
}
fn push_kind_map_entry(
key_bytes: &[u8],
value_bytes: &[u8],
context: *mut (),
) -> Result<(), FieldDecodeError> {
let state = unsafe { &mut *context.cast::<KindMapDecodeState>() };
state.0.push((
decode_structural_field_by_kind_bytes(key_bytes, state.1)?,
decode_structural_field_by_kind_bytes(value_bytes, state.2)?,
));
Ok(())
}
fn validate_kind_array_item(item_bytes: &[u8], context: *mut ()) -> Result<(), FieldDecodeError> {
let kind = unsafe { *context.cast::<KindArrayValidateState>() };
validate_structural_field_by_kind_bytes(item_bytes, kind)
}
fn validate_kind_map_entry(
key_bytes: &[u8],
value_bytes: &[u8],
context: *mut (),
) -> Result<(), FieldDecodeError> {
let (key_kind, value_kind) = unsafe { *context.cast::<KindMapValidateState>() };
validate_structural_field_by_kind_bytes(key_bytes, key_kind)?;
validate_structural_field_by_kind_bytes(value_bytes, value_kind)
}
fn decode_list_bytes(raw_bytes: &[u8], inner: FieldKind) -> Result<Value, FieldDecodeError> {
let mut state = (Vec::new(), inner);
walk_cbor_array_items(
raw_bytes,
"expected CBOR array for list/set field",
"typed CBOR: trailing bytes after list/set field",
(&raw mut state).cast(),
push_kind_array_item,
)?;
Ok(Value::List(state.0))
}
fn decode_map_bytes(
raw_bytes: &[u8],
key_kind: FieldKind,
value_kind: FieldKind,
) -> Result<Value, FieldDecodeError> {
let mut state = (Vec::new(), key_kind, value_kind);
walk_cbor_map_entries(
raw_bytes,
"expected CBOR map for map field",
"typed CBOR: trailing bytes after map field",
(&raw mut state).cast(),
push_kind_map_entry,
)?;
Ok(normalize_map_entries_or_preserve(state.0))
}
fn validate_list_bytes(raw_bytes: &[u8], inner: FieldKind) -> Result<(), FieldDecodeError> {
let mut state = inner;
walk_cbor_array_items(
raw_bytes,
"expected CBOR array for list/set field",
"typed CBOR: trailing bytes after list/set field",
(&raw mut state).cast(),
validate_kind_array_item,
)
}
fn validate_map_bytes(
raw_bytes: &[u8],
key_kind: FieldKind,
value_kind: FieldKind,
) -> Result<(), FieldDecodeError> {
let mut state = (key_kind, value_kind);
walk_cbor_map_entries(
raw_bytes,
"expected CBOR map for map field",
"typed CBOR: trailing bytes after map field",
(&raw mut state).cast(),
validate_kind_map_entry,
)
}
fn decode_enum_bytes(
raw_bytes: &[u8],
path: &'static str,
variants: &'static [EnumVariantModel],
) -> Result<Value, FieldDecodeError> {
let (variant, payload_bytes) = parse_tagged_variant_payload_bytes(
raw_bytes,
"typed CBOR: truncated CBOR value",
"expected text or one-entry CBOR map for enum field",
"expected one-entry CBOR map for enum payload variant",
"typed CBOR: trailing bytes after enum field",
)?;
if let Some(payload_bytes) = payload_bytes {
let payload =
if let Some(variant_model) = variants.iter().find(|item| item.ident() == variant) {
if let Some(payload_kind) = variant_model.payload_kind() {
match variant_model.payload_storage_decode() {
FieldStorageDecode::ByKind => {
decode_structural_field_by_kind_bytes(payload_bytes, *payload_kind)?
}
FieldStorageDecode::Value => {
decode_structural_value_storage_bytes(payload_bytes)?
}
}
} else {
decode_untyped_enum_payload_bytes(payload_bytes)?
}
} else {
decode_untyped_enum_payload_bytes(payload_bytes)?
};
Ok(Value::Enum(
ValueEnum::new(variant, Some(path)).with_payload(payload),
))
} else {
Ok(Value::Enum(ValueEnum::new(variant, Some(path))))
}
}
fn validate_enum_bytes(
raw_bytes: &[u8],
variants: &'static [EnumVariantModel],
) -> Result<(), FieldDecodeError> {
let (variant, payload_bytes) = parse_tagged_variant_payload_bytes(
raw_bytes,
"typed CBOR: truncated CBOR value",
"expected text or one-entry CBOR map for enum field",
"expected one-entry CBOR map for enum payload variant",
"typed CBOR: trailing bytes after enum field",
)?;
let Some(payload_bytes) = payload_bytes else {
return Ok(());
};
if let Some(variant_model) = variants.iter().find(|item| item.ident() == variant)
&& let Some(payload_kind) = variant_model.payload_kind()
{
return match variant_model.payload_storage_decode() {
FieldStorageDecode::ByKind => {
validate_structural_field_by_kind_bytes(payload_bytes, *payload_kind)
}
FieldStorageDecode::Value => validate_structural_value_storage_bytes(payload_bytes),
};
}
validate_untyped_enum_payload_bytes(payload_bytes)
}
pub(super) fn decode_composite_field_by_kind_bytes(
raw_bytes: &[u8],
kind: FieldKind,
) -> Result<Value, FieldDecodeError> {
match kind {
FieldKind::Enum { path, variants } => decode_enum_bytes(raw_bytes, path, variants),
FieldKind::List(inner) | FieldKind::Set(inner) => decode_list_bytes(raw_bytes, *inner),
FieldKind::Map { key, value } => decode_map_bytes(raw_bytes, *key, *value),
FieldKind::Relation { key_kind, .. } => {
decode_structural_field_by_kind_bytes(raw_bytes, *key_kind)
}
FieldKind::Account
| FieldKind::Blob
| FieldKind::Bool
| FieldKind::Date
| FieldKind::Decimal { .. }
| FieldKind::Duration
| FieldKind::Float32
| FieldKind::Float64
| FieldKind::Int
| FieldKind::Int128
| FieldKind::IntBig
| FieldKind::Principal
| FieldKind::Structured { .. }
| FieldKind::Subaccount
| FieldKind::Text
| FieldKind::Timestamp
| FieldKind::Uint
| FieldKind::Uint128
| FieldKind::UintBig
| FieldKind::Ulid
| FieldKind::Unit => Err(FieldDecodeError::new(
"leaf field unexpectedly routed through composite decode",
)),
}
}
pub(super) fn validate_composite_field_by_kind_bytes(
raw_bytes: &[u8],
kind: FieldKind,
) -> Result<(), FieldDecodeError> {
match kind {
FieldKind::Enum { variants, .. } => validate_enum_bytes(raw_bytes, variants),
FieldKind::List(inner) | FieldKind::Set(inner) => validate_list_bytes(raw_bytes, *inner),
FieldKind::Map { key, value } => validate_map_bytes(raw_bytes, *key, *value),
FieldKind::Relation { key_kind, .. } => {
validate_structural_field_by_kind_bytes(raw_bytes, *key_kind)
}
FieldKind::Account
| FieldKind::Blob
| FieldKind::Bool
| FieldKind::Date
| FieldKind::Decimal { .. }
| FieldKind::Duration
| FieldKind::Float32
| FieldKind::Float64
| FieldKind::Int
| FieldKind::Int128
| FieldKind::IntBig
| FieldKind::Principal
| FieldKind::Structured { .. }
| FieldKind::Subaccount
| FieldKind::Text
| FieldKind::Timestamp
| FieldKind::Uint
| FieldKind::Uint128
| FieldKind::UintBig
| FieldKind::Ulid
| FieldKind::Unit => Err(FieldDecodeError::new(
"leaf field unexpectedly routed through composite decode",
)),
}
}