use crate::{
db::data::{CanonicalRow, RawRow, StructuralRowDecodeError, StructuralRowFieldBytes},
error::InternalError,
model::entity::EntityModel,
value::Value,
};
use std::borrow::Cow;
use crate::db::data::persisted_row::{
codec::ScalarSlotValueRef,
contract::{
canonical_row_from_payload_source, canonical_row_from_value_source,
decode_slot_value_from_bytes, encode_slot_value_from_value,
},
reader::StructuralSlotReader,
types::{
PersistedRow, SerializedStructuralFieldUpdate, SerializedStructuralPatch, SlotReader,
StructuralPatch, field_model_for_slot,
},
writer::CompleteSerializedPatchWriter,
};
struct SerializedPatchPayloads<'a> {
model: &'static EntityModel,
payloads: Vec<Option<&'a [u8]>>,
}
impl<'a> SerializedPatchPayloads<'a> {
fn new(
model: &'static EntityModel,
patch: &'a SerializedStructuralPatch,
) -> Result<Self, InternalError> {
let mut payloads = vec![None; model.fields().len()];
for entry in patch.entries() {
let slot = entry.slot().index();
field_model_for_slot(model, slot)?;
payloads[slot] = Some(entry.payload());
}
Ok(Self { model, payloads })
}
const fn model(&self) -> &'static EntityModel {
self.model
}
fn has(&self, slot: usize) -> bool {
self.payloads.get(slot).is_some_and(Option::is_some)
}
fn get(&self, slot: usize) -> Option<&[u8]> {
self.payloads.get(slot).copied().flatten()
}
fn required_complete_payload(&self, slot: usize) -> Result<&[u8], InternalError> {
self.get(slot).ok_or_else(|| {
InternalError::persisted_row_encode_failed(format!(
"serialized patch did not emit slot {slot} for entity '{}'",
self.model.path()
))
})
}
fn overlay_payload<'b>(
&'b self,
baseline: &'b StructuralRowFieldBytes<'_>,
slot: usize,
) -> Result<&'b [u8], InternalError> {
if let Some(payload) = self.get(slot) {
return Ok(payload);
}
baseline.field(slot).ok_or_else(|| {
InternalError::persisted_row_encode_failed(format!(
"slot {slot} is missing from the baseline row for entity '{}'",
self.model.path()
))
})
}
}
struct SerializedPatchSlotReader<'a> {
payloads: SerializedPatchPayloads<'a>,
decoded: Vec<Option<Value>>,
}
impl<'a> SerializedPatchSlotReader<'a> {
fn new(
model: &'static EntityModel,
patch: &'a SerializedStructuralPatch,
) -> Result<Self, InternalError> {
let payloads = SerializedPatchPayloads::new(model, patch)?;
let decoded = vec![None; model.fields().len()];
Ok(Self { payloads, decoded })
}
}
impl SlotReader for SerializedPatchSlotReader<'_> {
fn model(&self) -> &'static EntityModel {
self.payloads.model()
}
fn has(&self, slot: usize) -> bool {
self.payloads.has(slot)
}
fn get_bytes(&self, slot: usize) -> Option<&[u8]> {
self.payloads.get(slot)
}
fn get_scalar(&self, slot: usize) -> Result<Option<ScalarSlotValueRef<'_>>, InternalError> {
let Some(raw_value) = self.get_bytes(slot) else {
return Ok(None);
};
let field = field_model_for_slot(self.model(), slot)?;
let crate::model::field::LeafCodec::Scalar(codec) = field.leaf_codec() else {
return Ok(None);
};
crate::db::data::persisted_row::codec::decode_scalar_slot_value(
raw_value,
codec,
field.name(),
)
.map(Some)
}
fn get_value(&mut self, slot: usize) -> Result<Option<Value>, InternalError> {
if slot >= self.decoded.len() {
return Ok(None);
}
if self.decoded[slot].is_none()
&& let Some(raw_value) = self.get_bytes(slot)
{
self.decoded[slot] = Some(decode_slot_value_from_bytes(self.model(), slot, raw_value)?);
}
Ok(self.decoded[slot].clone())
}
}
pub(in crate::db) fn materialize_entity_from_serialized_structural_patch<E>(
patch: &SerializedStructuralPatch,
) -> Result<E, InternalError>
where
E: PersistedRow,
{
let mut slots = SerializedPatchSlotReader::new(E::MODEL, patch)?;
E::materialize_from_slots(&mut slots)
}
pub(in crate::db) fn canonical_row_from_complete_serialized_structural_patch(
model: &'static EntityModel,
patch: &SerializedStructuralPatch,
) -> Result<CanonicalRow, InternalError> {
let patch_payloads = SerializedPatchPayloads::new(model, patch)?;
canonical_row_from_payload_source(model, |slot| patch_payloads.required_complete_payload(slot))
}
pub(in crate::db) fn canonical_row_from_entity<E>(entity: &E) -> Result<CanonicalRow, InternalError>
where
E: PersistedRow,
{
let serialized_slots = serialize_entity_slots_as_complete_serialized_patch(entity)?;
canonical_row_from_complete_serialized_structural_patch(E::MODEL, &serialized_slots)
}
pub(in crate::db) fn canonical_row_from_structural_slot_reader(
row_fields: &StructuralSlotReader<'_>,
) -> Result<CanonicalRow, InternalError> {
canonical_row_from_value_source(row_fields.model(), |slot| {
row_fields
.required_cached_value(slot)
.map(Cow::Borrowed)
.map_err(|_| {
InternalError::persisted_row_encode_failed(format!(
"slot {slot} is missing from the structural value cache for entity '{}'",
row_fields.model().path()
))
})
})
}
pub(in crate::db) fn canonical_row_from_raw_row(
model: &'static EntityModel,
raw_row: &RawRow,
) -> Result<CanonicalRow, InternalError> {
let field_bytes = StructuralRowFieldBytes::from_raw_row(raw_row, model)
.map_err(StructuralRowDecodeError::into_internal_error)?;
canonical_row_from_payload_source(model, |slot| {
field_bytes.field(slot).ok_or_else(|| {
InternalError::persisted_row_encode_failed(format!(
"slot {slot} is missing from the baseline row for entity '{}'",
model.path()
))
})
})
}
pub(in crate::db) const fn canonical_row_from_stored_raw_row(raw_row: RawRow) -> CanonicalRow {
CanonicalRow::from_canonical_raw_row(raw_row)
}
pub(in crate::db) fn serialize_structural_patch_fields(
model: &'static EntityModel,
patch: &StructuralPatch,
) -> Result<SerializedStructuralPatch, InternalError> {
if patch.is_empty() {
return Ok(SerializedStructuralPatch::default());
}
let mut entries = Vec::with_capacity(patch.entries().len());
for entry in patch.entries() {
let slot = entry.slot();
let payload = encode_slot_value_from_value(model, slot.index(), entry.value())?;
entries.push(SerializedStructuralFieldUpdate::new(slot, payload));
}
Ok(SerializedStructuralPatch::new(entries))
}
pub(in crate::db) fn serialize_entity_slots_as_complete_serialized_patch<E>(
entity: &E,
) -> Result<SerializedStructuralPatch, InternalError>
where
E: PersistedRow,
{
let mut writer = CompleteSerializedPatchWriter::for_model(E::MODEL);
entity.write_slots(&mut writer)?;
writer.finish_dense_slot_image()
}
pub(in crate::db) fn apply_serialized_structural_patch_to_raw_row(
model: &'static EntityModel,
raw_row: &RawRow,
patch: &SerializedStructuralPatch,
) -> Result<CanonicalRow, InternalError> {
if patch.is_empty() {
return canonical_row_from_raw_row(model, raw_row);
}
let field_bytes = StructuralRowFieldBytes::from_raw_row(raw_row, model)
.map_err(StructuralRowDecodeError::into_internal_error)?;
let patch_payloads = SerializedPatchPayloads::new(model, patch)?;
canonical_row_from_payload_source(model, |slot| {
patch_payloads.overlay_payload(&field_bytes, slot)
})
}