confium_tc_core/
message.rs1use std::fmt;
14
15#[derive(Debug, Clone, PartialEq, Eq)]
23pub struct Message {
24 pub from_party_id: String,
25 pub to_party_id: Option<String>,
26 pub round: u8,
27 pub payload: Vec<u8>,
28}
29
30impl Message {
31 pub fn directed(
33 from: impl Into<String>,
34 to: impl Into<String>,
35 round: u8,
36 payload: impl Into<Vec<u8>>,
37 ) -> Self {
38 Message {
39 from_party_id: from.into(),
40 to_party_id: Some(to.into()),
41 round,
42 payload: payload.into(),
43 }
44 }
45
46 pub fn broadcast(from: impl Into<String>, round: u8, payload: impl Into<Vec<u8>>) -> Self {
48 Message {
49 from_party_id: from.into(),
50 to_party_id: None,
51 round,
52 payload: payload.into(),
53 }
54 }
55
56 pub fn is_broadcast(&self) -> bool {
58 self.to_party_id.is_none()
59 }
60
61 pub fn is_directed(&self) -> bool {
63 self.to_party_id.is_some()
64 }
65
66 pub fn is_for(&self, party_id: &str) -> bool {
69 match &self.to_party_id {
70 None => true,
71 Some(to) => to == party_id,
72 }
73 }
74}
75
76impl fmt::Display for Message {
77 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78 let to = self.to_party_id.as_deref().unwrap_or("*");
79 write!(
80 f,
81 "round {} {} -> {} ({} bytes)",
82 self.round,
83 self.from_party_id,
84 to,
85 self.payload.len()
86 )
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93
94 #[test]
95 fn broadcast_message_has_null_recipient() {
96 let m = Message::broadcast("node-1", 1, [0xAA, 0xBB]);
97 assert!(m.is_broadcast());
98 assert!(!m.is_directed());
99 assert!(m.to_party_id.is_none());
100 }
101
102 #[test]
103 fn directed_message_has_recipient() {
104 let m = Message::directed("node-1", "node-2", 2, [0x01]);
105 assert!(!m.is_broadcast());
106 assert!(m.is_directed());
107 assert_eq!(m.to_party_id.as_deref(), Some("node-2"));
108 }
109
110 #[test]
111 fn is_for_matches_directed_recipient() {
112 let m = Message::directed("a", "b", 1, []);
113 assert!(m.is_for("b"));
114 assert!(!m.is_for("a"));
115 assert!(!m.is_for("c"));
116 }
117
118 #[test]
119 fn is_for_matches_everyone_when_broadcast() {
120 let m = Message::broadcast("a", 1, []);
121 assert!(m.is_for("b"));
122 assert!(m.is_for("c"));
123 assert!(m.is_for("a"));
126 }
127
128 #[test]
129 fn display_format_includes_round_and_parties() {
130 let m = Message::directed("a", "b", 3, [0u8; 4]);
131 assert_eq!(format!("{m}"), "round 3 a -> b (4 bytes)");
132 }
133
134 #[test]
135 fn display_format_uses_star_for_broadcast() {
136 let m = Message::broadcast("a", 1, [0u8; 2]);
137 assert_eq!(format!("{m}"), "round 1 a -> * (2 bytes)");
138 }
139
140 #[test]
141 fn equality_compares_all_fields() {
142 let a = Message::directed("p1", "p2", 1, vec![1, 2]);
143 let b = Message::directed("p1", "p2", 1, vec![1, 2]);
144 assert_eq!(a, b);
145
146 let c = Message::directed("p1", "p2", 2, vec![1, 2]);
147 assert_ne!(a, c);
148
149 let d = Message::directed("p1", "p3", 1, vec![1, 2]);
150 assert_ne!(a, d);
151 }
152}