use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Message {
pub from_party_id: String,
pub to_party_id: Option<String>,
pub round: u8,
pub payload: Vec<u8>,
}
impl Message {
pub fn directed(
from: impl Into<String>,
to: impl Into<String>,
round: u8,
payload: impl Into<Vec<u8>>,
) -> Self {
Message {
from_party_id: from.into(),
to_party_id: Some(to.into()),
round,
payload: payload.into(),
}
}
pub fn broadcast(from: impl Into<String>, round: u8, payload: impl Into<Vec<u8>>) -> Self {
Message {
from_party_id: from.into(),
to_party_id: None,
round,
payload: payload.into(),
}
}
pub fn is_broadcast(&self) -> bool {
self.to_party_id.is_none()
}
pub fn is_directed(&self) -> bool {
self.to_party_id.is_some()
}
pub fn is_for(&self, party_id: &str) -> bool {
match &self.to_party_id {
None => true,
Some(to) => to == party_id,
}
}
}
impl fmt::Display for Message {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let to = self.to_party_id.as_deref().unwrap_or("*");
write!(
f,
"round {} {} -> {} ({} bytes)",
self.round,
self.from_party_id,
to,
self.payload.len()
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn broadcast_message_has_null_recipient() {
let m = Message::broadcast("node-1", 1, [0xAA, 0xBB]);
assert!(m.is_broadcast());
assert!(!m.is_directed());
assert!(m.to_party_id.is_none());
}
#[test]
fn directed_message_has_recipient() {
let m = Message::directed("node-1", "node-2", 2, [0x01]);
assert!(!m.is_broadcast());
assert!(m.is_directed());
assert_eq!(m.to_party_id.as_deref(), Some("node-2"));
}
#[test]
fn is_for_matches_directed_recipient() {
let m = Message::directed("a", "b", 1, []);
assert!(m.is_for("b"));
assert!(!m.is_for("a"));
assert!(!m.is_for("c"));
}
#[test]
fn is_for_matches_everyone_when_broadcast() {
let m = Message::broadcast("a", 1, []);
assert!(m.is_for("b"));
assert!(m.is_for("c"));
assert!(m.is_for("a"));
}
#[test]
fn display_format_includes_round_and_parties() {
let m = Message::directed("a", "b", 3, [0u8; 4]);
assert_eq!(format!("{m}"), "round 3 a -> b (4 bytes)");
}
#[test]
fn display_format_uses_star_for_broadcast() {
let m = Message::broadcast("a", 1, [0u8; 2]);
assert_eq!(format!("{m}"), "round 1 a -> * (2 bytes)");
}
#[test]
fn equality_compares_all_fields() {
let a = Message::directed("p1", "p2", 1, vec![1, 2]);
let b = Message::directed("p1", "p2", 1, vec![1, 2]);
assert_eq!(a, b);
let c = Message::directed("p1", "p2", 2, vec![1, 2]);
assert_ne!(a, c);
let d = Message::directed("p1", "p3", 1, vec![1, 2]);
assert_ne!(a, d);
}
}