use std::cmp::Ordering;
use std::fmt;
use serde::{Deserialize, Serialize};
use super::error::VersionError;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct WireVersion {
pub major: u8,
pub minor: u16,
}
impl WireVersion {
pub const fn new(major: u8, minor: u16) -> Self {
Self { major, minor }
}
pub const fn is_compatible_with(&self, other: WireVersion) -> bool {
self.major == other.major
}
pub fn check_can_replay(self, event: WireVersion) -> Result<(), VersionError> {
if self.major != event.major {
return Err(VersionError::WireMajorMismatch { node: self, event });
}
Ok(())
}
}
impl Ord for WireVersion {
fn cmp(&self, other: &Self) -> Ordering {
self.major
.cmp(&other.major)
.then_with(|| self.minor.cmp(&other.minor))
}
}
impl PartialOrd for WireVersion {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl fmt::Display for WireVersion {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}.{}", self.major, self.minor)
}
}
pub const CURRENT_WIRE_VERSION: WireVersion = WireVersion::new(1, 0);
pub const MIN_SUPPORTED_WIRE_VERSION: WireVersion = WireVersion::new(1, 0);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ordering_is_lexicographic_major_then_minor() {
assert!(WireVersion::new(1, 0) < WireVersion::new(1, 1));
assert!(WireVersion::new(1, 99) < WireVersion::new(2, 0));
assert!(WireVersion::new(2, 0) > WireVersion::new(1, 999));
}
#[test]
fn compatibility_is_major_only() {
assert!(WireVersion::new(1, 0).is_compatible_with(WireVersion::new(1, 50)));
assert!(WireVersion::new(1, 50).is_compatible_with(WireVersion::new(1, 0)));
assert!(!WireVersion::new(1, 50).is_compatible_with(WireVersion::new(2, 0)));
assert!(!WireVersion::new(2, 0).is_compatible_with(WireVersion::new(1, 50)));
}
#[test]
fn check_can_replay_accepts_same_major() {
let node = WireVersion::new(1, 5);
assert!(node.check_can_replay(WireVersion::new(1, 0)).is_ok());
assert!(node.check_can_replay(WireVersion::new(1, 99)).is_ok());
}
#[test]
fn check_can_replay_rejects_different_major_with_structured_error() {
let node = WireVersion::new(1, 5);
let event = WireVersion::new(2, 0);
let err = node.check_can_replay(event).unwrap_err();
match err {
VersionError::WireMajorMismatch { node: n, event: e } => {
assert_eq!(n, node);
assert_eq!(e, event);
}
other => panic!("wrong error variant: {other:?}"),
}
}
#[test]
fn display_uses_dotted_form() {
assert_eq!(format!("{}", WireVersion::new(1, 5)), "1.5");
assert_eq!(format!("{}", WireVersion::new(0, 0)), "0.0");
}
#[test]
fn current_is_at_least_min_supported() {
assert!(CURRENT_WIRE_VERSION >= MIN_SUPPORTED_WIRE_VERSION);
assert!(CURRENT_WIRE_VERSION.is_compatible_with(MIN_SUPPORTED_WIRE_VERSION));
}
#[test]
fn serde_round_trip_compact() {
let v = WireVersion::new(3, 42);
let json = serde_json::to_string(&v).unwrap();
let back: WireVersion = serde_json::from_str(&json).unwrap();
assert_eq!(v, back);
}
}