use std::collections::BTreeSet;
use std::fmt;
use serde::{Deserialize, Serialize};
use tsoracle_consensus::AdvancePayload;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SetFormatVersionPayload {
pub target: u8,
pub gated_members: BTreeSet<u64>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum HighWaterCommand {
Advance(AdvancePayload),
SetFormatVersion(SetFormatVersionPayload),
AdvanceDense {
key: tsoracle_core::SeqKey,
count: u32,
},
}
impl fmt::Display for HighWaterCommand {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
HighWaterCommand::Advance(AdvancePayload { at_least }) => {
write!(f, "Advance {{ at_least: {at_least} }}")
}
HighWaterCommand::SetFormatVersion(SetFormatVersionPayload {
target,
gated_members,
}) => {
let rendered: Vec<String> = gated_members.iter().map(|id| id.to_string()).collect();
write!(
f,
"SetFormatVersion {{ target: {target}, gated_members: [{}] }}",
rendered.join(", ")
)
}
HighWaterCommand::AdvanceDense { key, count } => {
write!(
f,
"AdvanceDense {{ key: {}, count: {count} }}",
key.as_str()
)
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn display_renders_advance() {
let cmd = HighWaterCommand::Advance(AdvancePayload { at_least: 42 });
assert_eq!(format!("{cmd}"), "Advance { at_least: 42 }");
}
#[test]
fn display_renders_zero_at_least() {
let cmd = HighWaterCommand::Advance(AdvancePayload { at_least: 0 });
assert_eq!(format!("{cmd}"), "Advance { at_least: 0 }");
}
#[test]
fn postcard_round_trip_advance() {
let cmd = HighWaterCommand::Advance(AdvancePayload {
at_least: 1_234_567_890,
});
let bytes = postcard::to_stdvec(&cmd).expect("serialize");
let back: HighWaterCommand = postcard::from_bytes(&bytes).expect("deserialize");
assert_eq!(back, cmd);
}
#[test]
fn postcard_round_trip_zero() {
let cmd = HighWaterCommand::Advance(AdvancePayload { at_least: 0 });
let bytes = postcard::to_stdvec(&cmd).expect("serialize");
let back: HighWaterCommand = postcard::from_bytes(&bytes).expect("deserialize");
assert_eq!(back, cmd);
}
#[test]
fn postcard_round_trip_max() {
let cmd = HighWaterCommand::Advance(AdvancePayload { at_least: u64::MAX });
let bytes = postcard::to_stdvec(&cmd).expect("serialize");
let back: HighWaterCommand = postcard::from_bytes(&bytes).expect("deserialize");
assert_eq!(back, cmd);
}
#[test]
fn display_renders_set_format_version() {
let cmd = HighWaterCommand::SetFormatVersion(SetFormatVersionPayload {
target: 4,
gated_members: BTreeSet::from([1u64, 2u64, 3u64]),
});
assert_eq!(
format!("{cmd}"),
"SetFormatVersion { target: 4, gated_members: [1, 2, 3] }"
);
}
#[test]
fn postcard_round_trip_set_format_version() {
let cmd = HighWaterCommand::SetFormatVersion(SetFormatVersionPayload {
target: 7,
gated_members: BTreeSet::from([10u64, 20u64]),
});
let bytes = postcard::to_stdvec(&cmd).expect("serialize");
let back: HighWaterCommand = postcard::from_bytes(&bytes).expect("deserialize");
assert_eq!(back, cmd);
}
#[test]
fn postcard_round_trip_set_format_version_empty_gate() {
let cmd = HighWaterCommand::SetFormatVersion(SetFormatVersionPayload {
target: 4,
gated_members: BTreeSet::new(),
});
let bytes = postcard::to_stdvec(&cmd).expect("serialize");
let back: HighWaterCommand = postcard::from_bytes(&bytes).expect("deserialize");
assert_eq!(back, cmd);
}
#[test]
fn postcard_round_trip_advance_dense() {
let cmd = HighWaterCommand::AdvanceDense {
key: tsoracle_core::SeqKey::try_new("orders").unwrap(),
count: 7,
};
let bytes = postcard::to_stdvec(&cmd).expect("serialize");
let back: HighWaterCommand = postcard::from_bytes(&bytes).expect("deserialize");
assert_eq!(back, cmd);
}
#[test]
fn advance_dense_postcard_layout_is_pinned() {
let cmd = HighWaterCommand::AdvanceDense {
key: tsoracle_core::SeqKey::try_new("a").unwrap(),
count: 1,
};
let bytes = postcard::to_stdvec(&cmd).expect("serialize");
assert_eq!(bytes, vec![2u8, 1, b'a', 1]);
}
#[test]
fn decode_rejects_advance_dense_empty_key() {
let bytes = vec![2u8, 0, 1];
let decoded = postcard::from_bytes::<HighWaterCommand>(&bytes);
assert!(
decoded.is_err(),
"AdvanceDense with empty key must fail to decode, got {decoded:?}",
);
}
#[test]
fn decode_rejects_advance_dense_oversized_key() {
let mut bytes = vec![2u8, 0x81, 0x01];
bytes.extend(std::iter::repeat_n(b'a', 129));
bytes.push(1);
let decoded = postcard::from_bytes::<HighWaterCommand>(&bytes);
assert!(
decoded.is_err(),
"AdvanceDense with oversized key must fail to decode, got {decoded:?}",
);
}
}