use crate::{
db::data::persisted_row::types::{
FieldSlot, SerializedStructuralFieldUpdate, SerializedStructuralPatch, SlotWriter,
},
error::InternalError,
model::entity::EntityModel,
};
fn slot_cell_mut<T>(slots: &mut [T], slot: usize) -> Result<&mut T, InternalError> {
slots.get_mut(slot).ok_or_else(|| {
InternalError::persisted_row_encode_failed(format!("slot {slot} is outside the row layout"))
})
}
fn required_slot_payload_bytes<'a>(
model: &'static EntityModel,
writer_label: &str,
slot: usize,
payload: Option<&'a [u8]>,
) -> Result<&'a [u8], InternalError> {
payload.ok_or_else(|| {
InternalError::persisted_row_encode_failed(format!(
"{writer_label} cannot clear slot {slot} for entity '{}'",
model.path()
))
})
}
fn required_dense_slot_payloads(
model: &'static EntityModel,
writer_label: &str,
slots: Vec<StagedSlotPayload>,
) -> Result<Vec<Vec<u8>>, InternalError> {
let mut slot_payloads = Vec::with_capacity(slots.len());
for (slot, slot_payload) in slots.into_iter().enumerate() {
match slot_payload {
StagedSlotPayload::Set(bytes) => slot_payloads.push(bytes),
StagedSlotPayload::Missing => {
return Err(InternalError::persisted_row_encode_failed(format!(
"{writer_label} did not emit slot {slot} for entity '{}'",
model.path()
)));
}
}
}
Ok(slot_payloads)
}
#[derive(Clone, Debug, Eq, PartialEq)]
enum StagedSlotPayload {
Missing,
Set(Vec<u8>),
}
pub(in crate::db::data::persisted_row) struct CompleteSerializedPatchWriter {
model: &'static EntityModel,
slots: Vec<StagedSlotPayload>,
}
impl CompleteSerializedPatchWriter {
pub(in crate::db::data::persisted_row) fn for_model(model: &'static EntityModel) -> Self {
Self {
model,
slots: vec![StagedSlotPayload::Missing; model.fields().len()],
}
}
pub(in crate::db::data::persisted_row) fn finish_dense_slot_image(
self,
) -> Result<SerializedStructuralPatch, InternalError> {
let slot_payloads =
required_dense_slot_payloads(self.model, "serialized patch writer", self.slots)?;
let mut entries = Vec::with_capacity(slot_payloads.len());
for (slot, payload) in slot_payloads.into_iter().enumerate() {
let field_slot = FieldSlot::from_index(self.model, slot)?;
entries.push(SerializedStructuralFieldUpdate::new(field_slot, payload));
}
Ok(SerializedStructuralPatch::new(entries))
}
}
impl SlotWriter for CompleteSerializedPatchWriter {
fn write_slot(&mut self, slot: usize, payload: Option<&[u8]>) -> Result<(), InternalError> {
let entry = slot_cell_mut(self.slots.as_mut_slice(), slot)?;
let payload =
required_slot_payload_bytes(self.model, "serialized patch writer", slot, payload)?;
*entry = StagedSlotPayload::Set(payload.to_vec());
Ok(())
}
}