use crate::{
db::data::persisted_row::{
contract::encode_slot_payload_from_parts,
types::{FieldSlot, SerializedFieldUpdate, SerializedUpdatePatch, 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()
))
})
}
pub(in crate::db) struct SlotBufferWriter {
model: &'static EntityModel,
slots: Vec<SlotBufferSlot>,
}
impl SlotBufferWriter {
pub(in crate::db) fn for_model(model: &'static EntityModel) -> Self {
Self {
model,
slots: vec![SlotBufferSlot::Missing; model.fields().len()],
}
}
pub(in crate::db) fn finish(self) -> Result<Vec<u8>, InternalError> {
let slot_count = self.slots.len();
let mut payload_bytes = Vec::new();
let mut slot_table = Vec::with_capacity(slot_count);
for (slot, slot_payload) in self.slots.into_iter().enumerate() {
match slot_payload {
SlotBufferSlot::Set(bytes) => {
let start = u32::try_from(payload_bytes.len()).map_err(|_| {
InternalError::persisted_row_encode_failed(
"slot payload start exceeds u32 range",
)
})?;
let len = u32::try_from(bytes.len()).map_err(|_| {
InternalError::persisted_row_encode_failed(
"slot payload length exceeds u32 range",
)
})?;
payload_bytes.extend_from_slice(&bytes);
slot_table.push((start, len));
}
SlotBufferSlot::Missing => {
return Err(InternalError::persisted_row_encode_failed(format!(
"slot buffer writer did not emit slot {slot} for entity '{}'",
self.model.path()
)));
}
}
}
encode_slot_payload_from_parts(slot_count, slot_table.as_slice(), payload_bytes.as_slice())
}
}
impl SlotWriter for SlotBufferWriter {
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, "slot buffer writer", slot, payload)?;
*entry = SlotBufferSlot::Set(payload.to_vec());
Ok(())
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
enum SlotBufferSlot {
Missing,
Set(Vec<u8>),
}
pub(in crate::db::data::persisted_row) struct CompleteSerializedPatchWriter {
model: &'static EntityModel,
slots: Vec<PatchWriterSlot>,
}
impl CompleteSerializedPatchWriter {
pub(in crate::db::data::persisted_row) fn for_model(model: &'static EntityModel) -> Self {
Self {
model,
slots: vec![PatchWriterSlot::Missing; model.fields().len()],
}
}
pub(in crate::db::data::persisted_row) fn finish_dense_slot_image(
self,
) -> Result<SerializedUpdatePatch, InternalError> {
let mut entries = Vec::with_capacity(self.slots.len());
for (slot, payload) in self.slots.into_iter().enumerate() {
let field_slot = FieldSlot::from_index(self.model, slot)?;
let serialized = match payload {
PatchWriterSlot::Set(payload) => SerializedFieldUpdate::new(field_slot, payload),
PatchWriterSlot::Missing => {
return Err(InternalError::persisted_row_encode_failed(format!(
"serialized patch writer did not emit slot {slot} for entity '{}'",
self.model.path()
)));
}
};
entries.push(serialized);
}
Ok(SerializedUpdatePatch::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 = PatchWriterSlot::Set(payload.to_vec());
Ok(())
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
enum PatchWriterSlot {
Missing,
Set(Vec<u8>),
}