use ozzy_proto::{Opcode, layout::Fixed};
use crate::{
OpNumber, Prefix,
flow::{FlowError, Report},
};
use super::WireError;
pub const COMPACT_STATE_BYTES: usize = 29;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct CompactState {
pub handle: u32,
pub revision: u64,
pub received: OpNumber,
pub received_bytes: u64,
}
impl CompactState {
pub fn from_report(handle: u32, report: Report) -> Result<Self, WireError> {
report.validate_shape()?;
let state = Self {
handle,
revision: report.revision,
received: report.received.op,
received_bytes: report.received_bytes,
};
state.validate()?;
Ok(state)
}
fn validate(self) -> Result<(), WireError> {
if self.handle == 0 || self.revision == 0 || self.received.0 == u64::MAX {
return Err(FlowError::Report.into());
}
Ok(())
}
pub fn encode(self) -> Result<[u8; COMPACT_STATE_BYTES], WireError> {
self.validate()?;
let mut bytes = [0; COMPACT_STATE_BYTES];
bytes[0] = Opcode::ReplicaReceipt as u8;
bytes[1..5].copy_from_slice(&self.handle.to_be_bytes());
for (chunk, value) in bytes[5..].as_chunks_mut::<8>().0.iter_mut().zip([
self.revision,
self.received.0,
self.received_bytes,
]) {
chunk.copy_from_slice(&value.to_be_bytes());
}
Ok(bytes)
}
pub fn decode(bytes: &[u8]) -> Result<Self, WireError> {
let bytes: &[u8; COMPACT_STATE_BYTES] = bytes.try_into().map_err(|_| WireError::Length)?;
if bytes.u8_at::<0>() != Opcode::ReplicaReceipt as u8 {
return Err(WireError::UnsupportedCommand);
}
let state = Self {
handle: bytes.u32_at::<1>(),
revision: bytes.u64_at::<5>(),
received: OpNumber(bytes.u64_at::<13>()),
received_bytes: bytes.u64_at::<21>(),
};
state.validate()?;
Ok(state)
}
pub fn report(self, bound: Report, received: Prefix) -> Result<Report, WireError> {
self.validate()?;
if received.op != self.received {
return Err(WireError::History);
}
let report = Report {
revision: self.revision,
received,
received_bytes: self.received_bytes,
..bound
};
report.validate_shape()?;
Ok(report)
}
}