1#[cfg(not(feature = "std"))]
26use alloc::{collections::BTreeMap, string::String, string::ToString, vec, vec::Vec};
27#[cfg(feature = "std")]
28use std::collections::BTreeMap;
29
30use crate::NodeId;
31
32pub const MIN_VALID_TIMESTAMP: u64 = 1577836800000;
41
42pub const MAX_BATTERY_PERCENT: u8 = 100;
44
45pub const MIN_HEART_RATE: u8 = 20;
47
48pub const MAX_HEART_RATE: u8 = 250;
50
51pub const MAX_EMERGENCY_ACKS: usize = 256;
53
54pub const MAX_COUNTER_ENTRIES: usize = 256;
56
57pub type Timestamp = u64;
59
60#[derive(Debug, Clone, PartialEq)]
65pub struct LwwRegister<T: Clone> {
66 value: T,
68 timestamp: Timestamp,
70 node_id: NodeId,
72}
73
74impl<T: Clone + Default> Default for LwwRegister<T> {
75 fn default() -> Self {
76 Self {
77 value: T::default(),
78 timestamp: 0,
79 node_id: NodeId::default(),
80 }
81 }
82}
83
84impl<T: Clone> LwwRegister<T> {
85 pub fn new(value: T, timestamp: Timestamp, node_id: NodeId) -> Self {
87 Self {
88 value,
89 timestamp,
90 node_id,
91 }
92 }
93
94 pub fn get(&self) -> &T {
96 &self.value
97 }
98
99 pub fn timestamp(&self) -> Timestamp {
101 self.timestamp
102 }
103
104 pub fn node_id(&self) -> &NodeId {
106 &self.node_id
107 }
108
109 pub fn set(&mut self, value: T, timestamp: Timestamp, node_id: NodeId) -> bool {
113 if self.should_update(timestamp, &node_id) {
114 self.value = value;
115 self.timestamp = timestamp;
116 self.node_id = node_id;
117 true
118 } else {
119 false
120 }
121 }
122
123 pub fn merge(&mut self, other: &LwwRegister<T>) -> bool {
127 if self.should_update(other.timestamp, &other.node_id) {
128 self.value = other.value.clone();
129 self.timestamp = other.timestamp;
130 self.node_id = other.node_id;
131 true
132 } else {
133 false
134 }
135 }
136
137 fn should_update(&self, timestamp: Timestamp, node_id: &NodeId) -> bool {
139 timestamp > self.timestamp
140 || (timestamp == self.timestamp && node_id.as_u32() > self.node_id.as_u32())
141 }
142}
143
144#[derive(Debug, Clone, Default)]
149pub struct GCounter {
150 counts: BTreeMap<u32, u64>,
152}
153
154impl GCounter {
155 pub fn new() -> Self {
157 Self {
158 counts: BTreeMap::new(),
159 }
160 }
161
162 pub fn value(&self) -> u64 {
164 self.counts.values().sum()
165 }
166
167 pub fn increment(&mut self, node_id: &NodeId, amount: u64) {
169 let count = self.counts.entry(node_id.as_u32()).or_insert(0);
170 *count = count.saturating_add(amount);
171 }
172
173 pub fn node_count(&self, node_id: &NodeId) -> u64 {
175 self.counts.get(&node_id.as_u32()).copied().unwrap_or(0)
176 }
177
178 pub fn merge(&mut self, other: &GCounter) {
182 for (&node_id, &count) in &other.counts {
183 let our_count = self.counts.entry(node_id).or_insert(0);
184 *our_count = (*our_count).max(count);
185 }
186 }
187
188 pub fn node_count_total(&self) -> usize {
190 self.counts.len()
191 }
192
193 pub fn entries(&self) -> impl Iterator<Item = (u32, u64)> + '_ {
198 self.counts.iter().map(|(&k, &v)| (k, v))
199 }
200
201 pub fn encode(&self) -> Vec<u8> {
203 let mut buf = Vec::with_capacity(4 + self.counts.len() * 12);
204 buf.extend_from_slice(&(self.counts.len() as u32).to_le_bytes());
206 for (&node_id, &count) in &self.counts {
208 buf.extend_from_slice(&node_id.to_le_bytes());
209 buf.extend_from_slice(&count.to_le_bytes());
210 }
211 buf
212 }
213
214 pub fn decode(data: &[u8]) -> Option<Self> {
220 if data.len() < 4 {
221 return None;
222 }
223 let num_entries = u32::from_le_bytes([data[0], data[1], data[2], data[3]]) as usize;
224
225 if num_entries > MAX_COUNTER_ENTRIES {
227 return None;
228 }
229
230 if data.len() < 4 + num_entries * 12 {
231 return None;
232 }
233
234 let mut counts = BTreeMap::new();
235 let mut offset = 4;
236 for _ in 0..num_entries {
237 let node_id = u32::from_le_bytes([
238 data[offset],
239 data[offset + 1],
240 data[offset + 2],
241 data[offset + 3],
242 ]);
243 let count = u64::from_le_bytes([
244 data[offset + 4],
245 data[offset + 5],
246 data[offset + 6],
247 data[offset + 7],
248 data[offset + 8],
249 data[offset + 9],
250 data[offset + 10],
251 data[offset + 11],
252 ]);
253 if node_id != 0 {
255 counts.insert(node_id, count);
256 }
257 offset += 12;
258 }
259
260 Some(Self { counts })
261 }
262}
263
264#[derive(Debug, Clone, Default, PartialEq)]
266pub struct Position {
267 pub latitude: f32,
269 pub longitude: f32,
271 pub altitude: Option<f32>,
273 pub accuracy: Option<f32>,
275}
276
277impl Position {
278 pub fn new(latitude: f32, longitude: f32) -> Self {
280 Self {
281 latitude,
282 longitude,
283 altitude: None,
284 accuracy: None,
285 }
286 }
287
288 pub fn with_altitude(mut self, altitude: f32) -> Self {
290 self.altitude = Some(altitude);
291 self
292 }
293
294 pub fn with_accuracy(mut self, accuracy: f32) -> Self {
296 self.accuracy = Some(accuracy);
297 self
298 }
299
300 pub fn encode(&self) -> Vec<u8> {
302 let mut buf = Vec::with_capacity(20);
303 buf.extend_from_slice(&self.latitude.to_le_bytes());
304 buf.extend_from_slice(&self.longitude.to_le_bytes());
305
306 let mut flags = 0u8;
308 if self.altitude.is_some() {
309 flags |= 0x01;
310 }
311 if self.accuracy.is_some() {
312 flags |= 0x02;
313 }
314 buf.push(flags);
315
316 if let Some(alt) = self.altitude {
317 buf.extend_from_slice(&alt.to_le_bytes());
318 }
319 if let Some(acc) = self.accuracy {
320 buf.extend_from_slice(&acc.to_le_bytes());
321 }
322 buf
323 }
324
325 pub fn decode(data: &[u8]) -> Option<Self> {
331 if data.len() < 9 {
332 return None;
333 }
334
335 let latitude = f32::from_le_bytes([data[0], data[1], data[2], data[3]]);
336 let longitude = f32::from_le_bytes([data[4], data[5], data[6], data[7]]);
337 let flags = data[8];
338
339 if !latitude.is_finite() || !longitude.is_finite() {
341 return None;
342 }
343 if !(-90.0..=90.0).contains(&latitude) {
344 return None;
345 }
346 if !(-180.0..=180.0).contains(&longitude) {
347 return None;
348 }
349
350 let mut pos = Self::new(latitude, longitude);
351 let mut offset = 9;
352
353 if flags & 0x01 != 0 {
354 if data.len() < offset + 4 {
355 return None;
356 }
357 let alt = f32::from_le_bytes([
358 data[offset],
359 data[offset + 1],
360 data[offset + 2],
361 data[offset + 3],
362 ]);
363 if !alt.is_finite() || !(-1000.0..=100000.0).contains(&alt) {
365 return None;
366 }
367 pos.altitude = Some(alt);
368 offset += 4;
369 }
370
371 if flags & 0x02 != 0 {
372 if data.len() < offset + 4 {
373 return None;
374 }
375 let acc = f32::from_le_bytes([
376 data[offset],
377 data[offset + 1],
378 data[offset + 2],
379 data[offset + 3],
380 ]);
381 if !acc.is_finite() || acc < 0.0 {
383 return None;
384 }
385 pos.accuracy = Some(acc);
386 }
387
388 Some(pos)
389 }
390}
391
392#[derive(Debug, Clone, Default, PartialEq)]
394pub struct HealthStatus {
395 pub battery_percent: u8,
397 pub heart_rate: Option<u8>,
399 pub activity: u8,
404 pub alerts: u8,
406}
407
408impl HealthStatus {
409 pub const ALERT_MAN_DOWN: u8 = 0x01;
411 pub const ALERT_LOW_BATTERY: u8 = 0x02;
413 pub const ALERT_OUT_OF_RANGE: u8 = 0x04;
415 pub const ALERT_CUSTOM_1: u8 = 0x08;
417
418 pub fn new(battery_percent: u8) -> Self {
420 Self {
421 battery_percent,
422 heart_rate: None,
423 activity: 0,
424 alerts: 0,
425 }
426 }
427
428 pub fn with_heart_rate(mut self, hr: u8) -> Self {
430 self.heart_rate = Some(hr);
431 self
432 }
433
434 pub fn with_activity(mut self, activity: u8) -> Self {
436 self.activity = activity;
437 self
438 }
439
440 pub fn set_alert(&mut self, flag: u8) {
442 self.alerts |= flag;
443 }
444
445 pub fn clear_alert(&mut self, flag: u8) {
447 self.alerts &= !flag;
448 }
449
450 pub fn has_alert(&self, flag: u8) -> bool {
452 self.alerts & flag != 0
453 }
454
455 pub fn encode(&self) -> Vec<u8> {
457 vec![
458 self.battery_percent,
459 self.activity,
460 self.alerts,
461 self.heart_rate.unwrap_or(0),
463 ]
464 }
465
466 pub fn decode(data: &[u8]) -> Option<Self> {
480 if data.len() < 4 {
481 return None;
482 }
483
484 let battery_percent = data[0];
485 let activity = data[1];
486 let alerts = data[2];
487 let heart_rate_raw = data[3];
488
489 if battery_percent > MAX_BATTERY_PERCENT {
491 return None;
492 }
493
494 let mut status = Self::new(battery_percent);
503 status.activity = activity;
504 status.alerts = alerts;
505
506 if heart_rate_raw > 0 {
508 if !(MIN_HEART_RATE..=MAX_HEART_RATE).contains(&heart_rate_raw) {
509 return None;
510 }
511 status.heart_rate = Some(heart_rate_raw);
512 }
513
514 Some(status)
515 }
516}
517
518#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
524#[repr(u8)]
525pub enum PeripheralType {
526 #[default]
528 Unknown = 0,
529 SoldierSensor = 1,
531 FixedSensor = 2,
533 Relay = 3,
535}
536
537impl PeripheralType {
538 pub fn from_u8(v: u8) -> Self {
540 match v {
541 1 => Self::SoldierSensor,
542 2 => Self::FixedSensor,
543 3 => Self::Relay,
544 _ => Self::Unknown,
545 }
546 }
547}
548
549#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
551#[repr(u8)]
552pub enum EventType {
553 #[default]
555 None = 0,
556 Ping = 1,
558 NeedAssist = 2,
560 Emergency = 3,
562 Moving = 4,
564 InPosition = 5,
566 Ack = 6,
568}
569
570impl EventType {
571 pub fn from_u8(v: u8) -> Self {
573 match v {
574 1 => Self::Ping,
575 2 => Self::NeedAssist,
576 3 => Self::Emergency,
577 4 => Self::Moving,
578 5 => Self::InPosition,
579 6 => Self::Ack,
580 _ => Self::None,
581 }
582 }
583
584 pub fn label(&self) -> &'static str {
586 match self {
587 Self::None => "",
588 Self::Ping => "PING",
589 Self::NeedAssist => "NEED ASSIST",
590 Self::Emergency => "EMERGENCY",
591 Self::Moving => "MOVING",
592 Self::InPosition => "IN POSITION",
593 Self::Ack => "ACK",
594 }
595 }
596}
597
598#[derive(Debug, Clone, Default, PartialEq)]
600pub struct PeripheralEvent {
601 pub event_type: EventType,
603 pub timestamp: u64,
605}
606
607impl PeripheralEvent {
608 pub fn new(event_type: EventType, timestamp: u64) -> Self {
610 Self {
611 event_type,
612 timestamp,
613 }
614 }
615
616 pub fn encode(&self) -> Vec<u8> {
618 let mut buf = Vec::with_capacity(9);
619 buf.push(self.event_type as u8);
620 buf.extend_from_slice(&self.timestamp.to_le_bytes());
621 buf
622 }
623
624 pub fn decode(data: &[u8]) -> Option<Self> {
630 if data.len() < 9 {
631 return None;
632 }
633
634 let event_type = EventType::from_u8(data[0]);
635 let timestamp = u64::from_le_bytes([
636 data[1], data[2], data[3], data[4], data[5], data[6], data[7], data[8],
637 ]);
638
639 if timestamp != 0 && timestamp < MIN_VALID_TIMESTAMP {
642 return None;
643 }
644
645 Some(Self {
646 event_type,
647 timestamp,
648 })
649 }
650}
651
652#[derive(Debug, Clone, PartialEq, Default)]
676pub struct EmergencyEvent {
677 source_node: u32,
679 timestamp: u64,
681 acks: BTreeMap<u32, bool>,
683}
684
685impl EmergencyEvent {
686 pub fn new(source_node: u32, timestamp: u64, known_peers: &[u32]) -> Self {
695 let mut acks = BTreeMap::new();
696
697 acks.insert(source_node, true);
699
700 for &peer_id in known_peers {
702 if peer_id != source_node {
703 acks.entry(peer_id).or_insert(false);
704 }
705 }
706
707 Self {
708 source_node,
709 timestamp,
710 acks,
711 }
712 }
713
714 pub fn source_node(&self) -> u32 {
716 self.source_node
717 }
718
719 pub fn timestamp(&self) -> u64 {
721 self.timestamp
722 }
723
724 pub fn has_acked(&self, node_id: u32) -> bool {
726 self.acks.get(&node_id).copied().unwrap_or(false)
727 }
728
729 pub fn ack(&mut self, node_id: u32) -> bool {
733 let entry = self.acks.entry(node_id).or_insert(false);
734 if !*entry {
735 *entry = true;
736 true
737 } else {
738 false
739 }
740 }
741
742 pub fn add_peer(&mut self, node_id: u32) {
747 self.acks.entry(node_id).or_insert(false);
748 }
749
750 pub fn acked_nodes(&self) -> Vec<u32> {
752 self.acks
753 .iter()
754 .filter(|(_, &acked)| acked)
755 .map(|(&node_id, _)| node_id)
756 .collect()
757 }
758
759 pub fn pending_nodes(&self) -> Vec<u32> {
761 self.acks
762 .iter()
763 .filter(|(_, &acked)| !acked)
764 .map(|(&node_id, _)| node_id)
765 .collect()
766 }
767
768 pub fn all_nodes(&self) -> Vec<u32> {
770 self.acks.keys().copied().collect()
771 }
772
773 pub fn all_acked(&self) -> bool {
775 !self.acks.is_empty() && self.acks.values().all(|&acked| acked)
776 }
777
778 pub fn peer_count(&self) -> usize {
780 self.acks.len()
781 }
782
783 pub fn ack_count(&self) -> usize {
785 self.acks.values().filter(|&&acked| acked).count()
786 }
787
788 pub fn merge(&mut self, other: &EmergencyEvent) -> bool {
797 if self.source_node != other.source_node || self.timestamp != other.timestamp {
799 if other.timestamp > self.timestamp {
800 *self = other.clone();
801 return true;
802 }
803 return false;
804 }
805
806 let mut changed = false;
808 for (&node_id, &other_acked) in &other.acks {
809 let entry = self.acks.entry(node_id).or_insert(false);
810 if other_acked && !*entry {
811 *entry = true;
812 changed = true;
813 }
814 }
815 changed
816 }
817
818 pub fn encode(&self) -> Vec<u8> {
822 let mut buf = Vec::with_capacity(16 + self.acks.len() * 5);
823
824 buf.extend_from_slice(&self.source_node.to_le_bytes());
825 buf.extend_from_slice(&self.timestamp.to_le_bytes());
826 buf.extend_from_slice(&(self.acks.len() as u32).to_le_bytes());
827
828 for (&node_id, &acked) in &self.acks {
829 buf.extend_from_slice(&node_id.to_le_bytes());
830 buf.push(if acked { 1 } else { 0 });
831 }
832
833 buf
834 }
835
836 pub fn decode(data: &[u8]) -> Option<Self> {
842 if data.len() < 16 {
843 return None;
844 }
845
846 let source_node = u32::from_le_bytes([data[0], data[1], data[2], data[3]]);
847 let timestamp = u64::from_le_bytes([
848 data[4], data[5], data[6], data[7], data[8], data[9], data[10], data[11],
849 ]);
850 let num_acks = u32::from_le_bytes([data[12], data[13], data[14], data[15]]) as usize;
851
852 if source_node == 0 {
854 return None;
855 }
856
857 if timestamp < MIN_VALID_TIMESTAMP {
859 return None;
860 }
861
862 if num_acks > MAX_EMERGENCY_ACKS {
864 return None;
865 }
866
867 if data.len() < 16 + num_acks * 5 {
868 return None;
869 }
870
871 let mut acks = BTreeMap::new();
872 let mut offset = 16;
873 for _ in 0..num_acks {
874 let node_id = u32::from_le_bytes([
875 data[offset],
876 data[offset + 1],
877 data[offset + 2],
878 data[offset + 3],
879 ]);
880 if node_id != 0 {
882 let acked = data[offset + 4] != 0;
883 acks.insert(node_id, acked);
884 }
885 offset += 5;
886 }
887
888 Some(Self {
889 source_node,
890 timestamp,
891 acks,
892 })
893 }
894}
895
896#[derive(Debug, Clone, Default)]
901pub struct Peripheral {
902 pub id: u32,
904 pub parent_node: u32,
906 pub peripheral_type: PeripheralType,
908 pub callsign: [u8; 12],
910 pub health: HealthStatus,
912 pub last_event: Option<PeripheralEvent>,
914 pub location: Option<Position>,
916 pub timestamp: u64,
918}
919
920impl Peripheral {
921 pub fn new(id: u32, peripheral_type: PeripheralType) -> Self {
923 Self {
924 id,
925 parent_node: 0,
926 peripheral_type,
927 callsign: [0u8; 12],
928 health: HealthStatus::default(),
929 last_event: None,
930 location: None,
931 timestamp: 0,
932 }
933 }
934
935 pub fn with_callsign(mut self, callsign: &str) -> Self {
937 let bytes = callsign.as_bytes();
938 let len = bytes.len().min(12);
939 self.callsign[..len].copy_from_slice(&bytes[..len]);
940 self
941 }
942
943 pub fn set_callsign(&mut self, callsign: &str) {
945 self.callsign = [0u8; 12];
946 let bytes = callsign.as_bytes();
947 let len = bytes.len().min(12);
948 self.callsign[..len].copy_from_slice(&bytes[..len]);
949 }
950
951 pub fn callsign_str(&self) -> &str {
953 let len = self.callsign.iter().position(|&b| b == 0).unwrap_or(12);
954 core::str::from_utf8(&self.callsign[..len]).unwrap_or("")
955 }
956
957 pub fn with_parent(mut self, parent_node: u32) -> Self {
959 self.parent_node = parent_node;
960 self
961 }
962
963 pub fn with_location(mut self, location: Position) -> Self {
965 self.location = Some(location);
966 self
967 }
968
969 pub fn set_location(&mut self, latitude: f32, longitude: f32, altitude: Option<f32>) {
971 let mut pos = Position::new(latitude, longitude);
972 if let Some(alt) = altitude {
973 pos = pos.with_altitude(alt);
974 }
975 self.location = Some(pos);
976 }
977
978 pub fn clear_location(&mut self) {
980 self.location = None;
981 }
982
983 pub fn set_event(&mut self, event_type: EventType, timestamp: u64) {
985 self.last_event = Some(PeripheralEvent::new(event_type, timestamp));
986 self.timestamp = timestamp;
987 }
988
989 pub fn clear_event(&mut self) {
991 self.last_event = None;
992 }
993
994 pub fn encode(&self) -> Vec<u8> {
998 let mut buf = Vec::with_capacity(64);
999 buf.extend_from_slice(&self.id.to_le_bytes());
1000 buf.extend_from_slice(&self.parent_node.to_le_bytes());
1001 buf.push(self.peripheral_type as u8);
1002 buf.extend_from_slice(&self.callsign);
1003 buf.extend_from_slice(&self.health.encode());
1004
1005 if let Some(ref event) = self.last_event {
1006 buf.push(1); buf.extend_from_slice(&event.encode());
1008 } else {
1009 buf.push(0); }
1011
1012 buf.extend_from_slice(&self.timestamp.to_le_bytes());
1013
1014 if let Some(ref location) = self.location {
1016 buf.push(1); buf.extend_from_slice(&location.encode());
1018 } else {
1019 buf.push(0); }
1021
1022 buf
1023 }
1024
1025 pub fn decode(data: &[u8]) -> Option<Self> {
1031 if data.len() < 34 {
1032 return None;
1033 }
1034
1035 let id = u32::from_le_bytes([data[0], data[1], data[2], data[3]]);
1036 let parent_node = u32::from_le_bytes([data[4], data[5], data[6], data[7]]);
1037 let peripheral_type = PeripheralType::from_u8(data[8]);
1038
1039 if id == 0 {
1041 return None;
1042 }
1043
1044 let mut callsign = [0u8; 12];
1045 callsign.copy_from_slice(&data[9..21]);
1046
1047 let callsign_len = callsign.iter().position(|&b| b == 0).unwrap_or(12);
1050 if callsign_len > 0 && core::str::from_utf8(&callsign[..callsign_len]).is_err() {
1051 return None;
1052 }
1053
1054 let health = HealthStatus::decode(&data[21..25])?;
1056
1057 let has_event = data[25] != 0;
1058 let (last_event, timestamp_offset) = if has_event {
1059 if data.len() < 43 {
1060 return None;
1061 }
1062 (PeripheralEvent::decode(&data[26..35]), 35)
1064 } else {
1065 (None, 26)
1066 };
1067
1068 if data.len() < timestamp_offset + 8 {
1069 return None;
1070 }
1071
1072 let timestamp = u64::from_le_bytes([
1073 data[timestamp_offset],
1074 data[timestamp_offset + 1],
1075 data[timestamp_offset + 2],
1076 data[timestamp_offset + 3],
1077 data[timestamp_offset + 4],
1078 data[timestamp_offset + 5],
1079 data[timestamp_offset + 6],
1080 data[timestamp_offset + 7],
1081 ]);
1082
1083 if timestamp != 0 && timestamp < MIN_VALID_TIMESTAMP {
1085 return None;
1086 }
1087
1088 let location_offset = timestamp_offset + 8;
1091 let location = if data.len() > location_offset {
1092 let has_location = data[location_offset] != 0;
1093 if has_location && data.len() > location_offset + 1 {
1094 Position::decode(&data[location_offset + 1..])
1095 } else {
1096 None
1097 }
1098 } else {
1099 None
1100 };
1101
1102 Some(Self {
1103 id,
1104 parent_node,
1105 peripheral_type,
1106 callsign,
1107 health,
1108 last_event,
1109 location,
1110 timestamp,
1111 })
1112 }
1113}
1114
1115#[cfg(feature = "legacy-chat")]
1121pub const CHAT_MAX_TEXT_LEN: usize = 128;
1123
1124#[cfg(feature = "legacy-chat")]
1125pub const CHAT_MAX_SENDER_LEN: usize = 12;
1127
1128#[cfg(feature = "legacy-chat")]
1129pub const CHAT_MAX_MESSAGES: usize = 32;
1133
1134#[cfg(feature = "legacy-chat")]
1135pub const CHAT_SYNC_LIMIT: usize = 8;
1141
1142#[cfg(feature = "legacy-chat")]
1143#[derive(Debug, Clone, PartialEq)]
1148pub struct ChatMessage {
1149 pub origin_node: u32,
1151 pub timestamp: u64,
1153 sender: [u8; CHAT_MAX_SENDER_LEN],
1155 sender_len: u8,
1156 text: [u8; CHAT_MAX_TEXT_LEN],
1158 text_len: u8,
1159 pub is_broadcast: bool,
1161 pub requires_ack: bool,
1163 pub reply_to_node: u32,
1165 pub reply_to_timestamp: u64,
1167}
1168
1169#[cfg(feature = "legacy-chat")]
1170impl Default for ChatMessage {
1171 fn default() -> Self {
1172 Self {
1173 origin_node: 0,
1174 timestamp: 0,
1175 sender: [0u8; CHAT_MAX_SENDER_LEN],
1176 sender_len: 0,
1177 text: [0u8; CHAT_MAX_TEXT_LEN],
1178 text_len: 0,
1179 is_broadcast: true,
1180 requires_ack: false,
1181 reply_to_node: 0,
1182 reply_to_timestamp: 0,
1183 }
1184 }
1185}
1186
1187#[cfg(feature = "legacy-chat")]
1188impl ChatMessage {
1189 pub fn new(origin_node: u32, timestamp: u64, sender: &str, text: &str) -> Self {
1191 let mut msg = Self {
1192 origin_node,
1193 timestamp,
1194 ..Default::default()
1195 };
1196 msg.set_sender(sender);
1197 msg.set_text(text);
1198 msg
1199 }
1200
1201 pub fn set_sender(&mut self, sender: &str) {
1203 let bytes = sender.as_bytes();
1204 let len = bytes.len().min(CHAT_MAX_SENDER_LEN);
1205 self.sender[..len].copy_from_slice(&bytes[..len]);
1206 self.sender_len = len as u8;
1207 }
1208
1209 pub fn sender(&self) -> &str {
1211 core::str::from_utf8(&self.sender[..self.sender_len as usize]).unwrap_or("")
1212 }
1213
1214 pub fn set_text(&mut self, text: &str) {
1216 let bytes = text.as_bytes();
1217 let len = bytes.len().min(CHAT_MAX_TEXT_LEN);
1218 self.text[..len].copy_from_slice(&bytes[..len]);
1219 self.text_len = len as u8;
1220 }
1221
1222 pub fn text(&self) -> &str {
1224 core::str::from_utf8(&self.text[..self.text_len as usize]).unwrap_or("")
1225 }
1226
1227 pub fn set_reply_to(&mut self, node: u32, timestamp: u64) {
1229 self.reply_to_node = node;
1230 self.reply_to_timestamp = timestamp;
1231 }
1232
1233 pub fn is_reply(&self) -> bool {
1235 self.reply_to_node != 0 || self.reply_to_timestamp != 0
1236 }
1237
1238 pub fn message_id(&self) -> u64 {
1243 ((self.origin_node as u64) << 32) | (self.timestamp & 0xFFFFFFFF)
1244 }
1245
1246 pub fn encode(&self) -> Vec<u8> {
1261 let size = 4 + 8 + 1 + self.sender_len as usize + 1 + self.text_len as usize + 1 + 4 + 8;
1262 let mut buf = Vec::with_capacity(size);
1263
1264 buf.extend_from_slice(&self.origin_node.to_le_bytes());
1265 buf.extend_from_slice(&self.timestamp.to_le_bytes());
1266 buf.push(self.sender_len);
1267 buf.extend_from_slice(&self.sender[..self.sender_len as usize]);
1268 buf.push(self.text_len);
1269 buf.extend_from_slice(&self.text[..self.text_len as usize]);
1270
1271 let mut flags = 0u8;
1272 if self.is_broadcast {
1273 flags |= 0x01;
1274 }
1275 if self.requires_ack {
1276 flags |= 0x02;
1277 }
1278 buf.push(flags);
1279
1280 buf.extend_from_slice(&self.reply_to_node.to_le_bytes());
1281 buf.extend_from_slice(&self.reply_to_timestamp.to_le_bytes());
1282
1283 buf
1284 }
1285
1286 pub fn decode(data: &[u8]) -> Option<(Self, usize)> {
1299 if data.len() < 14 {
1300 return None;
1302 }
1303
1304 let origin_node = u32::from_le_bytes([data[0], data[1], data[2], data[3]]);
1305 let timestamp = u64::from_le_bytes([
1306 data[4], data[5], data[6], data[7], data[8], data[9], data[10], data[11],
1307 ]);
1308
1309 if origin_node == 0 {
1311 return None;
1312 }
1313
1314 if timestamp < MIN_VALID_TIMESTAMP {
1316 return None;
1317 }
1318
1319 let sender_len = data[12] as usize;
1320 if sender_len > CHAT_MAX_SENDER_LEN || data.len() < 13 + sender_len + 1 {
1321 return None;
1322 }
1323
1324 if sender_len == 0 {
1326 return None;
1327 }
1328
1329 let mut sender = [0u8; CHAT_MAX_SENDER_LEN];
1330 sender[..sender_len].copy_from_slice(&data[13..13 + sender_len]);
1331
1332 if core::str::from_utf8(&sender[..sender_len]).is_err() {
1334 return None;
1335 }
1336
1337 let text_offset = 13 + sender_len;
1338 let text_len = data[text_offset] as usize;
1339 if text_len > CHAT_MAX_TEXT_LEN || data.len() < text_offset + 1 + text_len + 1 {
1340 return None;
1341 }
1342
1343 let mut text = [0u8; CHAT_MAX_TEXT_LEN];
1344 text[..text_len].copy_from_slice(&data[text_offset + 1..text_offset + 1 + text_len]);
1345
1346 if text_len > 0 && core::str::from_utf8(&text[..text_len]).is_err() {
1349 return None;
1350 }
1351
1352 let flags_offset = text_offset + 1 + text_len;
1353 let flags = data[flags_offset];
1354 let is_broadcast = flags & 0x01 != 0;
1355 let requires_ack = flags & 0x02 != 0;
1356
1357 let mut reply_to_node = 0u32;
1359 let mut reply_to_timestamp = 0u64;
1360 let mut total_len = flags_offset + 1;
1361
1362 if data.len() >= flags_offset + 1 + 12 {
1363 reply_to_node = u32::from_le_bytes([
1364 data[flags_offset + 1],
1365 data[flags_offset + 2],
1366 data[flags_offset + 3],
1367 data[flags_offset + 4],
1368 ]);
1369 reply_to_timestamp = u64::from_le_bytes([
1370 data[flags_offset + 5],
1371 data[flags_offset + 6],
1372 data[flags_offset + 7],
1373 data[flags_offset + 8],
1374 data[flags_offset + 9],
1375 data[flags_offset + 10],
1376 data[flags_offset + 11],
1377 data[flags_offset + 12],
1378 ]);
1379 total_len = flags_offset + 13;
1380 }
1381
1382 Some((
1383 Self {
1384 origin_node,
1385 timestamp,
1386 sender,
1387 sender_len: sender_len as u8,
1388 text,
1389 text_len: text_len as u8,
1390 is_broadcast,
1391 requires_ack,
1392 reply_to_node,
1393 reply_to_timestamp,
1394 },
1395 total_len,
1396 ))
1397 }
1398}
1399
1400#[cfg(feature = "legacy-chat")]
1401#[derive(Debug, Clone, Default)]
1421pub struct ChatCRDT {
1422 messages: BTreeMap<u64, ChatMessage>,
1424}
1425
1426#[cfg(feature = "legacy-chat")]
1427impl ChatCRDT {
1428 pub fn new() -> Self {
1430 Self {
1431 messages: BTreeMap::new(),
1432 }
1433 }
1434
1435 pub fn add_message(&mut self, message: ChatMessage) -> bool {
1439 let id = message.message_id();
1440 if self.messages.contains_key(&id) {
1441 return false;
1442 }
1443
1444 self.messages.insert(id, message);
1445 self.prune_if_needed();
1446 true
1447 }
1448
1449 pub fn send_message(
1451 &mut self,
1452 origin_node: u32,
1453 timestamp: u64,
1454 sender: &str,
1455 text: &str,
1456 ) -> bool {
1457 let msg = ChatMessage::new(origin_node, timestamp, sender, text);
1458 self.add_message(msg)
1459 }
1460
1461 pub fn get_message(&self, origin_node: u32, timestamp: u64) -> Option<&ChatMessage> {
1463 let id = ((origin_node as u64) << 32) | (timestamp & 0xFFFFFFFF);
1464 self.messages.get(&id)
1465 }
1466
1467 pub fn messages(&self) -> impl Iterator<Item = &ChatMessage> {
1469 self.messages.values()
1470 }
1471
1472 pub fn messages_since(&self, since_timestamp: u64) -> impl Iterator<Item = &ChatMessage> {
1474 self.messages
1475 .values()
1476 .filter(move |m| m.timestamp > since_timestamp)
1477 }
1478
1479 pub fn len(&self) -> usize {
1481 self.messages.len()
1482 }
1483
1484 pub fn is_empty(&self) -> bool {
1486 self.messages.is_empty()
1487 }
1488
1489 pub fn newest_timestamp(&self) -> Option<u64> {
1491 self.messages.values().map(|m| m.timestamp).max()
1492 }
1493
1494 pub fn merge(&mut self, other: &ChatCRDT) -> bool {
1498 let mut changed = false;
1499 for (id, msg) in &other.messages {
1500 if !self.messages.contains_key(id) {
1501 self.messages.insert(*id, msg.clone());
1502 changed = true;
1503 }
1504 }
1505 if changed {
1506 self.prune_if_needed();
1507 }
1508 changed
1509 }
1510
1511 fn prune_if_needed(&mut self) {
1513 while self.messages.len() > CHAT_MAX_MESSAGES {
1514 if let Some(&oldest_id) = self.messages.keys().next() {
1516 self.messages.remove(&oldest_id);
1517 }
1518 }
1519 }
1520
1521 pub fn encode(&self) -> Vec<u8> {
1523 let mut buf = Vec::new();
1524
1525 buf.extend_from_slice(&(self.messages.len() as u16).to_le_bytes());
1527
1528 for msg in self.messages.values() {
1530 buf.extend_from_slice(&msg.encode());
1531 }
1532
1533 buf
1534 }
1535
1536 pub fn decode(data: &[u8]) -> Option<Self> {
1538 if data.len() < 2 {
1539 return None;
1540 }
1541
1542 let num_messages = u16::from_le_bytes([data[0], data[1]]) as usize;
1543 let mut messages = BTreeMap::new();
1544 let mut offset = 2;
1545
1546 for _ in 0..num_messages {
1547 if offset >= data.len() {
1548 break;
1549 }
1550 if let Some((msg, len)) = ChatMessage::decode(&data[offset..]) {
1551 let id = msg.message_id();
1552 messages.insert(id, msg);
1553 offset += len;
1554 } else {
1555 break;
1556 }
1557 }
1558
1559 Some(Self { messages })
1560 }
1561
1562 pub fn encoded_size(&self) -> usize {
1564 2 + self
1565 .messages
1566 .values()
1567 .map(|m| m.encode().len())
1568 .sum::<usize>()
1569 }
1570
1571 pub fn for_sync(&self) -> Self {
1579 if self.messages.len() <= CHAT_SYNC_LIMIT {
1580 return self.clone();
1581 }
1582
1583 let messages: BTreeMap<u64, ChatMessage> = self
1586 .messages
1587 .iter()
1588 .rev()
1589 .take(CHAT_SYNC_LIMIT)
1590 .map(|(&k, v)| (k, v.clone()))
1591 .collect();
1592
1593 Self { messages }
1594 }
1595}
1596
1597#[derive(Debug, Clone)]
1599pub enum CrdtOperation {
1600 UpdatePosition {
1602 node_id: NodeId,
1604 position: Position,
1606 timestamp: Timestamp,
1608 },
1609 UpdateHealth {
1611 node_id: NodeId,
1613 status: HealthStatus,
1615 timestamp: Timestamp,
1617 },
1618 IncrementCounter {
1620 counter_id: u8,
1622 node_id: NodeId,
1624 amount: u64,
1626 },
1627 UpdateRegister {
1629 key: String,
1631 value: Vec<u8>,
1633 timestamp: Timestamp,
1635 node_id: NodeId,
1637 },
1638}
1639
1640impl CrdtOperation {
1641 pub fn size(&self) -> usize {
1643 match self {
1644 CrdtOperation::UpdatePosition { position, .. } => 4 + 8 + position.encode().len(),
1645 CrdtOperation::UpdateHealth { status, .. } => 4 + 8 + status.encode().len(),
1646 CrdtOperation::IncrementCounter { .. } => 1 + 4 + 8,
1647 CrdtOperation::UpdateRegister { key, value, .. } => 4 + 8 + key.len() + value.len(),
1648 }
1649 }
1650
1651 pub fn encode(&self) -> Vec<u8> {
1653 let mut buf = Vec::new();
1654 match self {
1655 CrdtOperation::UpdatePosition {
1656 node_id,
1657 position,
1658 timestamp,
1659 } => {
1660 buf.push(0x01); buf.extend_from_slice(&node_id.as_u32().to_le_bytes());
1662 buf.extend_from_slice(×tamp.to_le_bytes());
1663 buf.extend_from_slice(&position.encode());
1664 }
1665 CrdtOperation::UpdateHealth {
1666 node_id,
1667 status,
1668 timestamp,
1669 } => {
1670 buf.push(0x02); buf.extend_from_slice(&node_id.as_u32().to_le_bytes());
1672 buf.extend_from_slice(×tamp.to_le_bytes());
1673 buf.extend_from_slice(&status.encode());
1674 }
1675 CrdtOperation::IncrementCounter {
1676 counter_id,
1677 node_id,
1678 amount,
1679 } => {
1680 buf.push(0x03); buf.push(*counter_id);
1682 buf.extend_from_slice(&node_id.as_u32().to_le_bytes());
1683 buf.extend_from_slice(&amount.to_le_bytes());
1684 }
1685 CrdtOperation::UpdateRegister {
1686 key,
1687 value,
1688 timestamp,
1689 node_id,
1690 } => {
1691 buf.push(0x04); buf.extend_from_slice(&node_id.as_u32().to_le_bytes());
1693 buf.extend_from_slice(×tamp.to_le_bytes());
1694 buf.push(key.len() as u8);
1695 buf.extend_from_slice(key.as_bytes());
1696 buf.extend_from_slice(&(value.len() as u16).to_le_bytes());
1697 buf.extend_from_slice(value);
1698 }
1699 }
1700 buf
1701 }
1702
1703 pub fn decode(data: &[u8]) -> Option<Self> {
1705 if data.is_empty() {
1706 return None;
1707 }
1708
1709 match data[0] {
1710 0x01 => {
1711 if data.len() < 13 {
1713 return None;
1714 }
1715 let node_id = NodeId::new(u32::from_le_bytes([data[1], data[2], data[3], data[4]]));
1716 let timestamp = u64::from_le_bytes([
1717 data[5], data[6], data[7], data[8], data[9], data[10], data[11], data[12],
1718 ]);
1719 let position = Position::decode(&data[13..])?;
1720 Some(CrdtOperation::UpdatePosition {
1721 node_id,
1722 position,
1723 timestamp,
1724 })
1725 }
1726 0x02 => {
1727 if data.len() < 13 {
1729 return None;
1730 }
1731 let node_id = NodeId::new(u32::from_le_bytes([data[1], data[2], data[3], data[4]]));
1732 let timestamp = u64::from_le_bytes([
1733 data[5], data[6], data[7], data[8], data[9], data[10], data[11], data[12],
1734 ]);
1735 let status = HealthStatus::decode(&data[13..])?;
1736 Some(CrdtOperation::UpdateHealth {
1737 node_id,
1738 status,
1739 timestamp,
1740 })
1741 }
1742 0x03 => {
1743 if data.len() < 14 {
1745 return None;
1746 }
1747 let counter_id = data[1];
1748 let node_id = NodeId::new(u32::from_le_bytes([data[2], data[3], data[4], data[5]]));
1749 let amount = u64::from_le_bytes([
1750 data[6], data[7], data[8], data[9], data[10], data[11], data[12], data[13],
1751 ]);
1752 Some(CrdtOperation::IncrementCounter {
1753 counter_id,
1754 node_id,
1755 amount,
1756 })
1757 }
1758 0x04 => {
1759 if data.len() < 14 {
1761 return None;
1762 }
1763 let node_id = NodeId::new(u32::from_le_bytes([data[1], data[2], data[3], data[4]]));
1764 let timestamp = u64::from_le_bytes([
1765 data[5], data[6], data[7], data[8], data[9], data[10], data[11], data[12],
1766 ]);
1767 let key_len = data[13] as usize;
1768 if data.len() < 14 + key_len + 2 {
1769 return None;
1770 }
1771 let key = core::str::from_utf8(&data[14..14 + key_len])
1772 .ok()?
1773 .to_string();
1774 let value_len =
1775 u16::from_le_bytes([data[14 + key_len], data[15 + key_len]]) as usize;
1776 if data.len() < 16 + key_len + value_len {
1777 return None;
1778 }
1779 let value = data[16 + key_len..16 + key_len + value_len].to_vec();
1780 Some(CrdtOperation::UpdateRegister {
1781 key,
1782 value,
1783 timestamp,
1784 node_id,
1785 })
1786 }
1787 _ => None,
1788 }
1789 }
1790}
1791
1792#[cfg(test)]
1793mod tests {
1794 use super::*;
1795
1796 const TEST_TIMESTAMP: u64 = 1705276800000;
1799
1800 #[test]
1801 fn test_lww_register_basic() {
1802 let mut reg = LwwRegister::new(42u32, 100, NodeId::new(1));
1803 assert_eq!(*reg.get(), 42);
1804 assert_eq!(reg.timestamp(), 100);
1805
1806 assert!(reg.set(99, 200, NodeId::new(2)));
1808 assert_eq!(*reg.get(), 99);
1809
1810 assert!(!reg.set(50, 150, NodeId::new(3)));
1812 assert_eq!(*reg.get(), 99);
1813 }
1814
1815 #[test]
1816 fn test_lww_register_tiebreak() {
1817 let mut reg = LwwRegister::new(1u32, 100, NodeId::new(1));
1818
1819 assert!(reg.set(2, 100, NodeId::new(2)));
1821 assert_eq!(*reg.get(), 2);
1822
1823 assert!(!reg.set(3, 100, NodeId::new(1)));
1825 assert_eq!(*reg.get(), 2);
1826 }
1827
1828 #[test]
1829 fn test_lww_register_merge() {
1830 let mut reg1 = LwwRegister::new(1u32, 100, NodeId::new(1));
1831 let reg2 = LwwRegister::new(2u32, 200, NodeId::new(2));
1832
1833 assert!(reg1.merge(®2));
1834 assert_eq!(*reg1.get(), 2);
1835 }
1836
1837 #[test]
1838 fn test_gcounter_basic() {
1839 let mut counter = GCounter::new();
1840 let node1 = NodeId::new(1);
1841 let node2 = NodeId::new(2);
1842
1843 counter.increment(&node1, 5);
1844 counter.increment(&node2, 3);
1845 counter.increment(&node1, 2);
1846
1847 assert_eq!(counter.value(), 10);
1848 assert_eq!(counter.node_count(&node1), 7);
1849 assert_eq!(counter.node_count(&node2), 3);
1850 }
1851
1852 #[test]
1853 fn test_gcounter_merge() {
1854 let mut counter1 = GCounter::new();
1855 let mut counter2 = GCounter::new();
1856 let node1 = NodeId::new(1);
1857 let node2 = NodeId::new(2);
1858
1859 counter1.increment(&node1, 5);
1860 counter2.increment(&node1, 3);
1861 counter2.increment(&node2, 4);
1862
1863 counter1.merge(&counter2);
1864
1865 assert_eq!(counter1.value(), 9); assert_eq!(counter1.node_count(&node1), 5);
1867 assert_eq!(counter1.node_count(&node2), 4);
1868 }
1869
1870 #[test]
1871 fn test_gcounter_encode_decode() {
1872 let mut counter = GCounter::new();
1873 counter.increment(&NodeId::new(1), 5);
1874 counter.increment(&NodeId::new(2), 10);
1875
1876 let encoded = counter.encode();
1877 let decoded = GCounter::decode(&encoded).unwrap();
1878
1879 assert_eq!(decoded.value(), counter.value());
1880 assert_eq!(decoded.node_count(&NodeId::new(1)), 5);
1881 assert_eq!(decoded.node_count(&NodeId::new(2)), 10);
1882 }
1883
1884 #[test]
1885 fn test_position_encode_decode() {
1886 let pos = Position::new(37.7749, -122.4194)
1887 .with_altitude(100.0)
1888 .with_accuracy(5.0);
1889
1890 let encoded = pos.encode();
1891 let decoded = Position::decode(&encoded).unwrap();
1892
1893 assert_eq!(decoded.latitude, pos.latitude);
1894 assert_eq!(decoded.longitude, pos.longitude);
1895 assert_eq!(decoded.altitude, pos.altitude);
1896 assert_eq!(decoded.accuracy, pos.accuracy);
1897 }
1898
1899 #[test]
1900 fn test_position_minimal_encode() {
1901 let pos = Position::new(0.0, 0.0);
1902 let encoded = pos.encode();
1903 assert_eq!(encoded.len(), 9); let pos_with_alt = Position::new(0.0, 0.0).with_altitude(0.0);
1906 let encoded_alt = pos_with_alt.encode();
1907 assert_eq!(encoded_alt.len(), 13);
1908 }
1909
1910 #[test]
1911 fn test_health_status() {
1912 let mut status = HealthStatus::new(85).with_heart_rate(72).with_activity(1);
1913
1914 assert_eq!(status.battery_percent, 85);
1915 assert_eq!(status.heart_rate, Some(72));
1916 assert!(!status.has_alert(HealthStatus::ALERT_MAN_DOWN));
1917
1918 status.set_alert(HealthStatus::ALERT_MAN_DOWN);
1919 assert!(status.has_alert(HealthStatus::ALERT_MAN_DOWN));
1920
1921 let encoded = status.encode();
1922 let decoded = HealthStatus::decode(&encoded).unwrap();
1923 assert_eq!(decoded.battery_percent, 85);
1924 assert_eq!(decoded.heart_rate, Some(72));
1925 assert!(decoded.has_alert(HealthStatus::ALERT_MAN_DOWN));
1926 }
1927
1928 #[test]
1929 fn test_health_status_decode_unknown_activity_preserves_peripheral() {
1930 let status = HealthStatus::new(87).with_activity(4);
1936 let encoded = status.encode();
1937 let decoded = HealthStatus::decode(&encoded)
1938 .expect("activity=4 must roundtrip without rejecting the peripheral");
1939 assert_eq!(decoded.battery_percent, 87);
1940 assert_eq!(decoded.activity, 4);
1941 }
1942
1943 #[test]
1944 fn test_crdt_operation_position() {
1945 let op = CrdtOperation::UpdatePosition {
1946 node_id: NodeId::new(0x1234),
1947 position: Position::new(37.0, -122.0),
1948 timestamp: 1000,
1949 };
1950
1951 let encoded = op.encode();
1952 let decoded = CrdtOperation::decode(&encoded).unwrap();
1953
1954 if let CrdtOperation::UpdatePosition {
1955 node_id,
1956 position,
1957 timestamp,
1958 } = decoded
1959 {
1960 assert_eq!(node_id.as_u32(), 0x1234);
1961 assert_eq!(timestamp, 1000);
1962 assert_eq!(position.latitude, 37.0);
1963 } else {
1964 panic!("Wrong operation type");
1965 }
1966 }
1967
1968 #[test]
1969 fn test_crdt_operation_counter() {
1970 let op = CrdtOperation::IncrementCounter {
1971 counter_id: 1,
1972 node_id: NodeId::new(0x5678),
1973 amount: 42,
1974 };
1975
1976 let encoded = op.encode();
1977 let decoded = CrdtOperation::decode(&encoded).unwrap();
1978
1979 if let CrdtOperation::IncrementCounter {
1980 counter_id,
1981 node_id,
1982 amount,
1983 } = decoded
1984 {
1985 assert_eq!(counter_id, 1);
1986 assert_eq!(node_id.as_u32(), 0x5678);
1987 assert_eq!(amount, 42);
1988 } else {
1989 panic!("Wrong operation type");
1990 }
1991 }
1992
1993 #[test]
1994 fn test_crdt_operation_size() {
1995 let pos_op = CrdtOperation::UpdatePosition {
1996 node_id: NodeId::new(1),
1997 position: Position::new(0.0, 0.0),
1998 timestamp: 0,
1999 };
2000 assert!(pos_op.size() > 0);
2001
2002 let counter_op = CrdtOperation::IncrementCounter {
2003 counter_id: 0,
2004 node_id: NodeId::new(1),
2005 amount: 1,
2006 };
2007 assert_eq!(counter_op.size(), 13);
2008 }
2009
2010 #[test]
2015 fn test_peripheral_type_from_u8() {
2016 assert_eq!(PeripheralType::from_u8(0), PeripheralType::Unknown);
2017 assert_eq!(PeripheralType::from_u8(1), PeripheralType::SoldierSensor);
2018 assert_eq!(PeripheralType::from_u8(2), PeripheralType::FixedSensor);
2019 assert_eq!(PeripheralType::from_u8(3), PeripheralType::Relay);
2020 assert_eq!(PeripheralType::from_u8(99), PeripheralType::Unknown);
2021 }
2022
2023 #[test]
2024 fn test_event_type_from_u8() {
2025 assert_eq!(EventType::from_u8(0), EventType::None);
2026 assert_eq!(EventType::from_u8(1), EventType::Ping);
2027 assert_eq!(EventType::from_u8(2), EventType::NeedAssist);
2028 assert_eq!(EventType::from_u8(3), EventType::Emergency);
2029 assert_eq!(EventType::from_u8(4), EventType::Moving);
2030 assert_eq!(EventType::from_u8(5), EventType::InPosition);
2031 assert_eq!(EventType::from_u8(6), EventType::Ack);
2032 assert_eq!(EventType::from_u8(99), EventType::None);
2033 }
2034
2035 #[test]
2036 fn test_event_type_labels() {
2037 assert_eq!(EventType::None.label(), "");
2038 assert_eq!(EventType::Emergency.label(), "EMERGENCY");
2039 assert_eq!(EventType::Ping.label(), "PING");
2040 }
2041
2042 #[test]
2043 fn test_peripheral_event_encode_decode() {
2044 let event = PeripheralEvent::new(EventType::Emergency, TEST_TIMESTAMP);
2045 let encoded = event.encode();
2046 assert_eq!(encoded.len(), 9);
2047
2048 let decoded = PeripheralEvent::decode(&encoded).unwrap();
2049 assert_eq!(decoded.event_type, EventType::Emergency);
2050 assert_eq!(decoded.timestamp, TEST_TIMESTAMP);
2051 }
2052
2053 #[test]
2054 fn test_peripheral_new() {
2055 let peripheral = Peripheral::new(0x12345678, PeripheralType::SoldierSensor);
2056 assert_eq!(peripheral.id, 0x12345678);
2057 assert_eq!(peripheral.peripheral_type, PeripheralType::SoldierSensor);
2058 assert_eq!(peripheral.parent_node, 0);
2059 assert!(peripheral.last_event.is_none());
2060 }
2061
2062 #[test]
2063 fn test_peripheral_with_callsign() {
2064 let peripheral = Peripheral::new(1, PeripheralType::SoldierSensor).with_callsign("ALPHA-1");
2065 assert_eq!(peripheral.callsign_str(), "ALPHA-1");
2066
2067 let peripheral2 = Peripheral::new(2, PeripheralType::SoldierSensor)
2069 .with_callsign("THIS_IS_A_VERY_LONG_CALLSIGN");
2070 assert_eq!(peripheral2.callsign_str(), "THIS_IS_A_VE");
2071 }
2072
2073 #[test]
2074 fn test_peripheral_set_event() {
2075 let mut peripheral = Peripheral::new(1, PeripheralType::SoldierSensor);
2076 peripheral.set_event(EventType::Emergency, TEST_TIMESTAMP);
2077
2078 assert!(peripheral.last_event.is_some());
2079 let event = peripheral.last_event.as_ref().unwrap();
2080 assert_eq!(event.event_type, EventType::Emergency);
2081 assert_eq!(event.timestamp, TEST_TIMESTAMP);
2082 assert_eq!(peripheral.timestamp, TEST_TIMESTAMP);
2083
2084 peripheral.clear_event();
2085 assert!(peripheral.last_event.is_none());
2086 }
2087
2088 #[test]
2089 fn test_peripheral_encode_decode_without_event() {
2090 let peripheral = Peripheral::new(0xAABBCCDD, PeripheralType::SoldierSensor)
2091 .with_callsign("BRAVO-2")
2092 .with_parent(0x11223344);
2093
2094 let encoded = peripheral.encode();
2095 assert_eq!(encoded.len(), 35); let decoded = Peripheral::decode(&encoded).unwrap();
2098 assert_eq!(decoded.id, 0xAABBCCDD);
2099 assert_eq!(decoded.parent_node, 0x11223344);
2100 assert_eq!(decoded.peripheral_type, PeripheralType::SoldierSensor);
2101 assert_eq!(decoded.callsign_str(), "BRAVO-2");
2102 assert!(decoded.last_event.is_none());
2103 assert!(decoded.location.is_none());
2104 }
2105
2106 #[test]
2107 fn test_peripheral_encode_decode_with_event() {
2108 let mut peripheral = Peripheral::new(0x12345678, PeripheralType::SoldierSensor)
2109 .with_callsign("CHARLIE")
2110 .with_parent(0x87654321);
2111 peripheral.health = HealthStatus::new(85);
2112 peripheral.set_event(EventType::NeedAssist, TEST_TIMESTAMP);
2113
2114 let encoded = peripheral.encode();
2115 assert_eq!(encoded.len(), 44); let decoded = Peripheral::decode(&encoded).unwrap();
2118 assert_eq!(decoded.id, 0x12345678);
2119 assert_eq!(decoded.parent_node, 0x87654321);
2120 assert_eq!(decoded.callsign_str(), "CHARLIE");
2121 assert_eq!(decoded.health.battery_percent, 85);
2122 assert!(decoded.last_event.is_some());
2123 let event = decoded.last_event.as_ref().unwrap();
2124 assert_eq!(event.event_type, EventType::NeedAssist);
2125 assert_eq!(event.timestamp, TEST_TIMESTAMP);
2126 assert!(decoded.location.is_none());
2127 }
2128
2129 #[test]
2130 fn test_peripheral_encode_decode_with_location() {
2131 let location = Position::new(37.7749, -122.4194).with_altitude(10.0);
2132 let peripheral = Peripheral::new(0x12345678, PeripheralType::SoldierSensor)
2133 .with_callsign("DELTA")
2134 .with_location(location);
2135
2136 let encoded = peripheral.encode();
2137 assert_eq!(encoded.len(), 48);
2139
2140 let decoded = Peripheral::decode(&encoded).unwrap();
2141 assert_eq!(decoded.id, 0x12345678);
2142 assert_eq!(decoded.callsign_str(), "DELTA");
2143 assert!(decoded.location.is_some());
2144
2145 let loc = decoded.location.unwrap();
2146 assert!((loc.latitude - 37.7749).abs() < 0.0001);
2147 assert!((loc.longitude - (-122.4194)).abs() < 0.0001);
2148 assert!(loc.altitude.is_some());
2149 assert!((loc.altitude.unwrap() - 10.0).abs() < 1.0);
2150 }
2151
2152 #[test]
2153 fn test_peripheral_decode_invalid_data() {
2154 assert!(Peripheral::decode(&[0u8; 10]).is_none());
2156
2157 let mut data = vec![0u8; 34];
2159 data[25] = 0; assert!(Peripheral::decode(&data).is_none()); data[0..4].copy_from_slice(&1u32.to_le_bytes()); assert!(Peripheral::decode(&data).is_some());
2165
2166 data[25] = 1; assert!(Peripheral::decode(&data).is_none());
2169 }
2170
2171 #[test]
2176 fn test_emergency_event_new() {
2177 let peers = vec![0x22222222, 0x33333333];
2178 let event = EmergencyEvent::new(0x11111111, TEST_TIMESTAMP, &peers);
2179
2180 assert_eq!(event.source_node(), 0x11111111);
2181 assert_eq!(event.timestamp(), TEST_TIMESTAMP);
2182 assert_eq!(event.peer_count(), 3); assert!(event.has_acked(0x11111111));
2186 assert!(!event.has_acked(0x22222222));
2188 assert!(!event.has_acked(0x33333333));
2189 }
2190
2191 #[test]
2192 fn test_emergency_event_ack() {
2193 let peers = vec![0x22222222, 0x33333333];
2194 let mut event = EmergencyEvent::new(0x11111111, TEST_TIMESTAMP, &peers);
2195
2196 assert_eq!(event.ack_count(), 1); assert!(!event.all_acked());
2198
2199 assert!(event.ack(0x22222222)); assert_eq!(event.ack_count(), 2);
2202 assert!(!event.all_acked());
2203
2204 assert!(!event.ack(0x22222222)); assert!(event.ack(0x33333333));
2209 assert_eq!(event.ack_count(), 3);
2210 assert!(event.all_acked());
2211 }
2212
2213 #[test]
2214 fn test_emergency_event_pending_nodes() {
2215 let peers = vec![0x22222222, 0x33333333];
2216 let mut event = EmergencyEvent::new(0x11111111, TEST_TIMESTAMP, &peers);
2217
2218 let pending = event.pending_nodes();
2219 assert_eq!(pending.len(), 2);
2220 assert!(pending.contains(&0x22222222));
2221 assert!(pending.contains(&0x33333333));
2222
2223 event.ack(0x22222222);
2224 let pending = event.pending_nodes();
2225 assert_eq!(pending.len(), 1);
2226 assert!(pending.contains(&0x33333333));
2227 }
2228
2229 #[test]
2230 fn test_emergency_event_encode_decode() {
2231 let peers = vec![0x22222222, 0x33333333];
2232 let mut event = EmergencyEvent::new(0x11111111, TEST_TIMESTAMP, &peers);
2233 event.ack(0x22222222);
2234
2235 let encoded = event.encode();
2236 let decoded = EmergencyEvent::decode(&encoded).unwrap();
2237
2238 assert_eq!(decoded.source_node(), 0x11111111);
2239 assert_eq!(decoded.timestamp(), TEST_TIMESTAMP);
2240 assert!(decoded.has_acked(0x11111111));
2241 assert!(decoded.has_acked(0x22222222));
2242 assert!(!decoded.has_acked(0x33333333));
2243 }
2244
2245 #[test]
2246 fn test_emergency_event_merge_same_event() {
2247 let peers = vec![0x22222222, 0x33333333];
2249 let mut event1 = EmergencyEvent::new(0x11111111, TEST_TIMESTAMP, &peers);
2250 let mut event2 = EmergencyEvent::new(0x11111111, TEST_TIMESTAMP, &peers);
2251
2252 event1.ack(0x22222222);
2253 event2.ack(0x33333333);
2254
2255 let changed = event1.merge(&event2);
2257 assert!(changed);
2258 assert!(event1.has_acked(0x22222222));
2259 assert!(event1.has_acked(0x33333333));
2260 assert!(event1.all_acked());
2261 }
2262
2263 #[test]
2264 fn test_emergency_event_merge_different_events() {
2265 let mut old_event = EmergencyEvent::new(0x11111111, TEST_TIMESTAMP, &[0x22222222]);
2267 old_event.ack(0x22222222);
2268
2269 let new_event =
2271 EmergencyEvent::new(0x33333333, TEST_TIMESTAMP + 1000, &[0x11111111, 0x22222222]);
2272
2273 let changed = old_event.merge(&new_event);
2275 assert!(changed);
2276 assert_eq!(old_event.source_node(), 0x33333333);
2277 assert_eq!(old_event.timestamp(), TEST_TIMESTAMP + 1000);
2278 assert!(!old_event.has_acked(0x22222222));
2280 }
2281
2282 #[test]
2283 fn test_emergency_event_merge_older_event_ignored() {
2284 let mut current = EmergencyEvent::new(0x11111111, TEST_TIMESTAMP + 1000, &[0x22222222]);
2286
2287 let older = EmergencyEvent::new(0x33333333, TEST_TIMESTAMP, &[0x11111111]);
2289
2290 let changed = current.merge(&older);
2292 assert!(!changed);
2293 assert_eq!(current.source_node(), 0x11111111);
2294 assert_eq!(current.timestamp(), TEST_TIMESTAMP + 1000);
2295 }
2296
2297 #[test]
2298 fn test_emergency_event_add_peer() {
2299 let mut event = EmergencyEvent::new(0x11111111, TEST_TIMESTAMP, &[]);
2300
2301 event.add_peer(0x22222222);
2303 assert!(!event.has_acked(0x22222222));
2304 assert_eq!(event.peer_count(), 2);
2305
2306 event.ack(0x22222222);
2308 event.add_peer(0x22222222);
2309 assert!(event.has_acked(0x22222222)); }
2311
2312 #[test]
2313 fn test_emergency_event_decode_invalid() {
2314 assert!(EmergencyEvent::decode(&[0u8; 10]).is_none());
2316
2317 let mut data = vec![0u8; 16];
2319 data[12] = 5; assert!(EmergencyEvent::decode(&data).is_none());
2321 }
2322
2323 #[cfg(feature = "legacy-chat")]
2328 mod chat_tests {
2329 use super::*;
2330
2331 #[test]
2332 fn test_chat_message_new() {
2333 let msg = ChatMessage::new(0x12345678, TEST_TIMESTAMP, "ALPHA-1", "Hello mesh!");
2334 assert_eq!(msg.origin_node, 0x12345678);
2335 assert_eq!(msg.timestamp, TEST_TIMESTAMP);
2336 assert_eq!(msg.sender(), "ALPHA-1");
2337 assert_eq!(msg.text(), "Hello mesh!");
2338 assert!(msg.is_broadcast);
2339 assert!(!msg.requires_ack);
2340 assert!(!msg.is_reply());
2341 }
2342
2343 #[test]
2344 fn test_chat_message_reply_to() {
2345 let mut msg =
2346 ChatMessage::new(0x12345678, TEST_TIMESTAMP + 1000, "BRAVO", "Roger that");
2347 msg.set_reply_to(0xAABBCCDD, TEST_TIMESTAMP);
2348
2349 assert!(msg.is_reply());
2350 assert_eq!(msg.reply_to_node, 0xAABBCCDD);
2351 assert_eq!(msg.reply_to_timestamp, TEST_TIMESTAMP);
2352 }
2353
2354 #[test]
2355 fn test_chat_message_truncation() {
2356 let msg = ChatMessage::new(0x1, TEST_TIMESTAMP, "VERY_LONG_CALLSIGN", "Hi");
2358 assert_eq!(msg.sender(), "VERY_LONG_CA"); let long_text = "A".repeat(200);
2362 let msg = ChatMessage::new(0x1, TEST_TIMESTAMP, "X", &long_text);
2363 assert_eq!(msg.text().len(), 128);
2364 }
2365
2366 #[test]
2367 fn test_chat_message_id() {
2368 let msg = ChatMessage::new(0x12345678, 0x18D4A51_ABCDEF01, "X", "Y");
2369 let id = msg.message_id();
2370 assert_eq!(id, (0x12345678u64 << 32) | 0xABCDEF01);
2372 }
2373
2374 #[test]
2375 fn test_chat_message_encode_decode() {
2376 let mut msg = ChatMessage::new(0x12345678, TEST_TIMESTAMP, "CHARLIE", "Test message");
2377 msg.is_broadcast = true;
2378 msg.requires_ack = true;
2379 msg.set_reply_to(0xAABBCCDD, TEST_TIMESTAMP - 1000);
2380
2381 let encoded = msg.encode();
2382 let (decoded, len) = ChatMessage::decode(&encoded).unwrap();
2383
2384 assert_eq!(len, encoded.len());
2385 assert_eq!(decoded.origin_node, 0x12345678);
2386 assert_eq!(decoded.timestamp, TEST_TIMESTAMP);
2387 assert_eq!(decoded.sender(), "CHARLIE");
2388 assert_eq!(decoded.text(), "Test message");
2389 assert!(decoded.is_broadcast);
2390 assert!(decoded.requires_ack);
2391 assert_eq!(decoded.reply_to_node, 0xAABBCCDD);
2392 assert_eq!(decoded.reply_to_timestamp, TEST_TIMESTAMP - 1000);
2393 }
2394
2395 #[test]
2396 fn test_chat_message_decode_empty_text() {
2397 let msg = ChatMessage::new(0x1, TEST_TIMESTAMP, "ACK-NODE", "");
2399 let encoded = msg.encode();
2400 let (decoded, _) = ChatMessage::decode(&encoded).unwrap();
2401 assert_eq!(decoded.sender(), "ACK-NODE");
2402 assert_eq!(decoded.text(), "");
2403 }
2404
2405 #[test]
2410 fn test_chat_message_decode_rejects_zero_origin() {
2411 let msg = ChatMessage::new(0x12345678, TEST_TIMESTAMP, "TEST", "msg");
2413 let mut encoded = msg.encode();
2414 encoded[0] = 0;
2416 encoded[1] = 0;
2417 encoded[2] = 0;
2418 encoded[3] = 0;
2419
2420 assert!(ChatMessage::decode(&encoded).is_none());
2421 }
2422
2423 #[test]
2424 fn test_chat_message_decode_rejects_old_timestamp() {
2425 let msg = ChatMessage::new(0x12345678, TEST_TIMESTAMP, "TEST", "msg");
2427 let mut encoded = msg.encode();
2428 let old_ts: u64 = 1000;
2430 encoded[4..12].copy_from_slice(&old_ts.to_le_bytes());
2431
2432 assert!(ChatMessage::decode(&encoded).is_none());
2433 }
2434
2435 #[test]
2436 fn test_chat_message_decode_rejects_empty_sender() {
2437 let msg = ChatMessage::new(0x12345678, TEST_TIMESTAMP, "X", "msg");
2439 let mut encoded = msg.encode();
2440 encoded[12] = 0;
2442 let mut raw = Vec::new();
2446 raw.extend_from_slice(&0x12345678u32.to_le_bytes()); raw.extend_from_slice(&TEST_TIMESTAMP.to_le_bytes()); raw.push(0); raw.push(3); raw.extend_from_slice(b"msg"); raw.push(0x01); assert!(ChatMessage::decode(&raw).is_none());
2454 }
2455
2456 #[test]
2457 fn test_chat_message_decode_rejects_invalid_utf8_sender() {
2458 let mut raw = Vec::new();
2460 raw.extend_from_slice(&0x12345678u32.to_le_bytes()); raw.extend_from_slice(&TEST_TIMESTAMP.to_le_bytes()); raw.push(4); raw.extend_from_slice(&[0x66, 0x59, 0xFF, 0xFE]); raw.push(3); raw.extend_from_slice(b"msg"); raw.push(0x01); raw.extend_from_slice(&0u32.to_le_bytes()); raw.extend_from_slice(&0u64.to_le_bytes()); assert!(ChatMessage::decode(&raw).is_none());
2471 }
2472
2473 #[test]
2474 fn test_chat_message_decode_rejects_invalid_utf8_text() {
2475 let mut raw = Vec::new();
2477 raw.extend_from_slice(&0x12345678u32.to_le_bytes()); raw.extend_from_slice(&TEST_TIMESTAMP.to_le_bytes()); raw.push(4); raw.extend_from_slice(b"TEST"); raw.push(4); raw.extend_from_slice(&[0x80, 0x81, 0x82, 0x83]); raw.push(0x01); raw.extend_from_slice(&0u32.to_le_bytes()); raw.extend_from_slice(&0u64.to_le_bytes()); assert!(ChatMessage::decode(&raw).is_none());
2488 }
2489
2490 #[test]
2491 fn test_chat_message_decode_accepts_valid_utf8() {
2492 let mut raw = Vec::new();
2494 raw.extend_from_slice(&0x12345678u32.to_le_bytes()); raw.extend_from_slice(&TEST_TIMESTAMP.to_le_bytes()); raw.push(6); raw.extend_from_slice("TËST".as_bytes()); let sender_bytes = "TËST1".as_bytes(); raw[12] = sender_bytes.len() as u8;
2501 raw.truncate(13);
2502 raw.extend_from_slice(sender_bytes);
2503 raw.push(4); raw.extend_from_slice(b"test"); raw.push(0x01); raw.extend_from_slice(&0u32.to_le_bytes()); raw.extend_from_slice(&0u64.to_le_bytes()); let result = ChatMessage::decode(&raw);
2510 assert!(result.is_some());
2511 let (msg, _) = result.unwrap();
2512 assert_eq!(msg.sender(), "TËST1");
2513 }
2514
2515 #[test]
2520 fn test_chat_crdt_new() {
2521 let chat = ChatCRDT::new();
2522 assert!(chat.is_empty());
2523 assert_eq!(chat.len(), 0);
2524 }
2525
2526 #[test]
2527 fn test_chat_crdt_add_message() {
2528 let mut chat = ChatCRDT::new();
2529
2530 let msg = ChatMessage::new(0x12345678, TEST_TIMESTAMP, "ALPHA", "Hello");
2531 assert!(chat.add_message(msg.clone()));
2532 assert_eq!(chat.len(), 1);
2533
2534 assert!(!chat.add_message(msg));
2536 assert_eq!(chat.len(), 1);
2537 }
2538
2539 #[test]
2540 fn test_chat_crdt_send_message() {
2541 let mut chat = ChatCRDT::new();
2542
2543 assert!(chat.send_message(0x1, TEST_TIMESTAMP, "ALPHA", "First"));
2544 assert!(chat.send_message(0x2, TEST_TIMESTAMP + 1, "BRAVO", "Second"));
2545 assert_eq!(chat.len(), 2);
2546
2547 assert!(!chat.send_message(0x1, TEST_TIMESTAMP, "ALPHA", "Duplicate"));
2549 assert_eq!(chat.len(), 2);
2550 }
2551
2552 #[test]
2553 fn test_chat_crdt_get_message() {
2554 let mut chat = ChatCRDT::new();
2555 chat.send_message(0x12345678, TEST_TIMESTAMP, "ALPHA", "Test");
2556
2557 let msg = chat.get_message(0x12345678, TEST_TIMESTAMP);
2558 assert!(msg.is_some());
2559 assert_eq!(msg.unwrap().text(), "Test");
2560
2561 assert!(chat.get_message(0x99999999, TEST_TIMESTAMP).is_none());
2563 }
2564
2565 #[test]
2566 fn test_chat_crdt_merge() {
2567 let mut chat1 = ChatCRDT::new();
2568 let mut chat2 = ChatCRDT::new();
2569
2570 chat1.send_message(0x1, TEST_TIMESTAMP, "ALPHA", "From 1");
2571 chat2.send_message(0x2, TEST_TIMESTAMP + 1, "BRAVO", "From 2");
2572
2573 let changed = chat1.merge(&chat2);
2575 assert!(changed);
2576 assert_eq!(chat1.len(), 2);
2577
2578 let changed = chat1.merge(&chat2);
2580 assert!(!changed);
2581 assert_eq!(chat1.len(), 2);
2582 }
2583
2584 #[test]
2585 fn test_chat_crdt_merge_duplicates() {
2586 let mut chat1 = ChatCRDT::new();
2587 let mut chat2 = ChatCRDT::new();
2588
2589 chat1.send_message(0x1, TEST_TIMESTAMP, "ALPHA", "Same message");
2591 chat2.send_message(0x1, TEST_TIMESTAMP, "ALPHA", "Same message");
2592
2593 chat1.merge(&chat2);
2595 assert_eq!(chat1.len(), 1);
2596 }
2597
2598 #[test]
2599 fn test_chat_crdt_pruning() {
2600 let mut chat = ChatCRDT::new();
2601
2602 for i in 0..(CHAT_MAX_MESSAGES + 10) {
2604 chat.send_message(i as u32 + 1, TEST_TIMESTAMP + i as u64, "X", "Y");
2605 }
2606
2607 assert_eq!(chat.len(), CHAT_MAX_MESSAGES);
2609
2610 assert!(chat.get_message(1, TEST_TIMESTAMP).is_none());
2614 assert!(chat.get_message(10, TEST_TIMESTAMP + 9).is_none());
2615 assert!(chat.get_message(11, TEST_TIMESTAMP + 10).is_some());
2617 }
2618
2619 #[test]
2620 fn test_chat_crdt_encode_decode() {
2621 let mut chat = ChatCRDT::new();
2622 chat.send_message(0x12345678, TEST_TIMESTAMP, "ALPHA", "First message");
2623 chat.send_message(0xAABBCCDD, TEST_TIMESTAMP + 1000, "BRAVO", "Second message");
2624
2625 let encoded = chat.encode();
2626 let decoded = ChatCRDT::decode(&encoded).unwrap();
2627
2628 assert_eq!(decoded.len(), 2);
2629 assert!(decoded.get_message(0x12345678, TEST_TIMESTAMP).is_some());
2630 assert!(decoded
2631 .get_message(0xAABBCCDD, TEST_TIMESTAMP + 1000)
2632 .is_some());
2633 }
2634
2635 #[test]
2636 fn test_chat_crdt_messages_since() {
2637 let mut chat = ChatCRDT::new();
2638 chat.send_message(0x1, TEST_TIMESTAMP, "A", "Old");
2639 chat.send_message(0x2, TEST_TIMESTAMP + 1000, "B", "Mid");
2640 chat.send_message(0x3, TEST_TIMESTAMP + 2000, "C", "New");
2641
2642 let recent: Vec<_> = chat.messages_since(TEST_TIMESTAMP + 500).collect();
2643 assert_eq!(recent.len(), 2);
2644 }
2645
2646 #[test]
2647 fn test_chat_crdt_newest_timestamp() {
2648 let mut chat = ChatCRDT::new();
2649 assert!(chat.newest_timestamp().is_none());
2650
2651 chat.send_message(0x1, TEST_TIMESTAMP, "A", "1");
2652 assert_eq!(chat.newest_timestamp(), Some(TEST_TIMESTAMP));
2653
2654 chat.send_message(0x2, TEST_TIMESTAMP + 2000, "B", "2");
2655 assert_eq!(chat.newest_timestamp(), Some(TEST_TIMESTAMP + 2000));
2656
2657 chat.send_message(0x3, TEST_TIMESTAMP + 1000, "C", "3"); assert_eq!(chat.newest_timestamp(), Some(TEST_TIMESTAMP + 2000));
2659 }
2660
2661 #[test]
2666 fn test_chat_crdt_decode_skips_invalid_messages() {
2667 let mut valid_chat = ChatCRDT::new();
2669 valid_chat.send_message(0x12345678, TEST_TIMESTAMP, "VALID", "Good message");
2670
2671 let encoded = valid_chat.encode();
2672
2673 let decoded = ChatCRDT::decode(&encoded).unwrap();
2675 assert_eq!(decoded.len(), 1);
2676 assert!(decoded.get_message(0x12345678, TEST_TIMESTAMP).is_some());
2677 }
2678
2679 #[test]
2680 fn test_chat_crdt_decode_handles_truncated_data() {
2681 let mut chat = ChatCRDT::new();
2682 chat.send_message(0x12345678, TEST_TIMESTAMP, "TEST", "Message");
2683
2684 let encoded = chat.encode();
2685
2686 let truncated = &encoded[..2];
2688 let decoded = ChatCRDT::decode(truncated);
2689 assert!(decoded.is_some());
2690 assert_eq!(decoded.unwrap().len(), 0); }
2692 } }