use tsoracle_codec::{CodecError, decode_postcard_exact, encode_postcard};
use tsoracle_openraft_toolkit::LogStoreCodec;
use crate::type_config::{OpenraftLogId, OpenraftVote, TypeConfig};
#[derive(Debug, Clone, Copy, Default)]
pub struct OpenraftLogCodec;
impl LogStoreCodec<TypeConfig> for OpenraftLogCodec {
fn encode_entry(
version: u8,
entry: &<TypeConfig as openraft::RaftTypeConfig>::Entry,
) -> Result<Vec<u8>, CodecError> {
if version < tsoracle_openraft_toolkit::BATCH_WRITE_VERSION {
if let openraft::EntryPayload::Normal(
crate::log_entry::HighWaterCommand::AdvanceDenseBatch { .. },
) = &entry.payload
{
return Err(CodecError::NotRepresentable { version });
}
}
encode_postcard(entry)
}
fn decode_entry(
_version: u8,
body: &[u8],
) -> Result<<TypeConfig as openraft::RaftTypeConfig>::Entry, CodecError> {
decode_postcard_exact(body)
}
fn encode_vote(_version: u8, vote: &OpenraftVote) -> Result<Vec<u8>, CodecError> {
encode_postcard(vote)
}
fn decode_vote(_version: u8, body: &[u8]) -> Result<OpenraftVote, CodecError> {
decode_postcard_exact(body)
}
fn encode_log_id(_version: u8, log_id: &OpenraftLogId) -> Result<Vec<u8>, CodecError> {
encode_postcard(log_id)
}
fn decode_log_id(_version: u8, body: &[u8]) -> Result<OpenraftLogId, CodecError> {
decode_postcard_exact(body)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::log_entry::HighWaterCommand;
use tsoracle_consensus::AdvancePayload;
#[test]
fn entry_body_matches_raw_postcard() {
let lid = openraft::testing::log_id::<TypeConfig>(1, 1, 1);
let entry: <TypeConfig as openraft::RaftTypeConfig>::Entry =
openraft::entry::RaftEntry::new_normal(
lid,
HighWaterCommand::Advance(AdvancePayload { at_least: 5 }),
);
let via_provider =
<OpenraftLogCodec as LogStoreCodec<TypeConfig>>::encode_entry(4, &entry).unwrap();
let raw = postcard::to_stdvec(&entry).unwrap();
assert_eq!(via_provider, raw);
}
#[test]
fn vote_body_matches_raw_postcard() {
let vote: OpenraftVote = openraft::Vote::new_committed(7, 3);
let via_provider =
<OpenraftLogCodec as LogStoreCodec<TypeConfig>>::encode_vote(4, &vote).unwrap();
assert_eq!(via_provider, postcard::to_stdvec(&vote).unwrap());
let back: OpenraftVote =
<OpenraftLogCodec as LogStoreCodec<TypeConfig>>::decode_vote(4, &via_provider).unwrap();
assert_eq!(back, vote);
}
#[test]
fn log_id_body_matches_raw_postcard() {
let log_id: OpenraftLogId = openraft::testing::log_id::<TypeConfig>(2, 1, 9);
let via_provider =
<OpenraftLogCodec as LogStoreCodec<TypeConfig>>::encode_log_id(4, &log_id).unwrap();
assert_eq!(via_provider, postcard::to_stdvec(&log_id).unwrap());
}
#[test]
fn encode_entry_rejects_batch_below_batch_version() {
use crate::log_entry::{DenseAdvance, HighWaterCommand};
use tsoracle_openraft_toolkit::{BATCH_WRITE_VERSION, DENSE_WRITE_VERSION};
let lid = openraft::testing::log_id::<TypeConfig>(1, 1, 1);
let entry: <TypeConfig as openraft::RaftTypeConfig>::Entry =
openraft::entry::RaftEntry::new_normal(
lid,
HighWaterCommand::AdvanceDenseBatch {
entries: vec![DenseAdvance {
key: tsoracle_core::SeqKey::try_new("k").unwrap(),
count: 1,
}],
},
);
use tsoracle_openraft_toolkit::BASELINE_WRITE_VERSION;
for below in [BASELINE_WRITE_VERSION, DENSE_WRITE_VERSION] {
assert!(
matches!(
<OpenraftLogCodec as LogStoreCodec<TypeConfig>>::encode_entry(below, &entry),
Err(CodecError::NotRepresentable { .. })
),
"AdvanceDenseBatch must be refused at write version {below}",
);
}
assert!(
<OpenraftLogCodec as LogStoreCodec<TypeConfig>>::encode_entry(
BATCH_WRITE_VERSION,
&entry
)
.is_ok()
);
}
}