1#[cfg(not(feature = "std"))]
43use alloc::vec::Vec;
44
45use crate::sync::crdt::{
46 ChatCRDT, EmergencyEvent, EventType, GCounter, Peripheral, PeripheralEvent,
47};
48use crate::NodeId;
49
50pub const EXTENDED_MARKER: u8 = 0xAB;
52
53pub const EMERGENCY_MARKER: u8 = 0xAC;
55
56pub const CHAT_MARKER: u8 = 0xAD;
67
68pub const ENCRYPTED_MARKER: u8 = 0xAE;
82
83pub const PEER_E2EE_MARKER: u8 = 0xAF;
98
99pub const KEY_EXCHANGE_MARKER: u8 = 0xB0;
110
111pub const RELAY_ENVELOPE_MARKER: u8 = 0xB1;
127
128pub const DELTA_DOCUMENT_MARKER: u8 = 0xB2;
143
144pub const MIN_DOCUMENT_SIZE: usize = 8;
146
147pub const MAX_MESH_SIZE: usize = 20;
153
154pub const TARGET_DOCUMENT_SIZE: usize = 244;
158
159pub const MAX_DOCUMENT_SIZE: usize = 512;
163
164#[derive(Debug, Clone)]
170pub struct HiveDocument {
171 pub version: u32,
173
174 pub node_id: NodeId,
176
177 pub counter: GCounter,
179
180 pub peripheral: Option<Peripheral>,
182
183 pub emergency: Option<EmergencyEvent>,
185
186 pub chat: Option<ChatCRDT>,
192}
193
194impl Default for HiveDocument {
195 fn default() -> Self {
196 Self {
197 version: 1,
198 node_id: NodeId::default(),
199 counter: GCounter::new(),
200 peripheral: None,
201 emergency: None,
202 chat: None,
203 }
204 }
205}
206
207impl HiveDocument {
208 pub fn new(node_id: NodeId) -> Self {
210 Self {
211 version: 1,
212 node_id,
213 counter: GCounter::new(),
214 peripheral: None,
215 emergency: None,
216 chat: None,
217 }
218 }
219
220 pub fn with_peripheral(mut self, peripheral: Peripheral) -> Self {
222 self.peripheral = Some(peripheral);
223 self
224 }
225
226 pub fn with_emergency(mut self, emergency: EmergencyEvent) -> Self {
228 self.emergency = Some(emergency);
229 self
230 }
231
232 pub fn with_chat(mut self, chat: ChatCRDT) -> Self {
234 self.chat = Some(chat);
235 self
236 }
237
238 pub fn increment_version(&mut self) {
240 self.version = self.version.wrapping_add(1);
241 }
242
243 pub fn increment_counter(&mut self) {
245 self.counter.increment(&self.node_id, 1);
246 self.increment_version();
247 }
248
249 pub fn set_event(&mut self, event_type: EventType, timestamp: u64) {
251 if let Some(ref mut peripheral) = self.peripheral {
252 peripheral.set_event(event_type, timestamp);
253 self.increment_counter();
254 }
255 }
256
257 pub fn clear_event(&mut self) {
259 if let Some(ref mut peripheral) = self.peripheral {
260 peripheral.clear_event();
261 self.increment_version();
262 }
263 }
264
265 pub fn set_emergency(&mut self, source_node: u32, timestamp: u64, known_peers: &[u32]) {
270 self.emergency = Some(EmergencyEvent::new(source_node, timestamp, known_peers));
271 self.increment_counter();
272 }
273
274 pub fn ack_emergency(&mut self, node_id: u32) -> bool {
278 if let Some(ref mut emergency) = self.emergency {
279 if emergency.ack(node_id) {
280 self.increment_version();
281 return true;
282 }
283 }
284 false
285 }
286
287 pub fn clear_emergency(&mut self) {
289 if self.emergency.is_some() {
290 self.emergency = None;
291 self.increment_version();
292 }
293 }
294
295 pub fn get_emergency(&self) -> Option<&EmergencyEvent> {
297 self.emergency.as_ref()
298 }
299
300 pub fn has_emergency(&self) -> bool {
302 self.emergency.is_some()
303 }
304
305 pub fn get_chat(&self) -> Option<&ChatCRDT> {
309 self.chat.as_ref()
310 }
311
312 pub fn get_or_create_chat(&mut self) -> &mut ChatCRDT {
314 if self.chat.is_none() {
315 self.chat = Some(ChatCRDT::new());
316 }
317 self.chat.as_mut().unwrap()
318 }
319
320 pub fn add_chat_message(
324 &mut self,
325 origin_node: u32,
326 timestamp: u64,
327 sender: &str,
328 text: &str,
329 ) -> bool {
330 use crate::sync::crdt::ChatMessage;
331
332 let mut msg = ChatMessage::new(origin_node, timestamp, sender, text);
333 msg.is_broadcast = true;
334
335 let chat = self.get_or_create_chat();
336 if chat.add_message(msg) {
337 self.increment_counter();
338 true
339 } else {
340 false
341 }
342 }
343
344 pub fn add_chat_reply(
346 &mut self,
347 origin_node: u32,
348 timestamp: u64,
349 sender: &str,
350 text: &str,
351 reply_to_node: u32,
352 reply_to_timestamp: u64,
353 ) -> bool {
354 use crate::sync::crdt::ChatMessage;
355
356 let mut msg = ChatMessage::new(origin_node, timestamp, sender, text);
357 msg.is_broadcast = true;
358 msg.set_reply_to(reply_to_node, reply_to_timestamp);
359
360 let chat = self.get_or_create_chat();
361 if chat.add_message(msg) {
362 self.increment_counter();
363 true
364 } else {
365 false
366 }
367 }
368
369 pub fn has_chat(&self) -> bool {
371 self.chat.as_ref().is_some_and(|c| !c.is_empty())
372 }
373
374 pub fn chat_count(&self) -> usize {
376 self.chat.as_ref().map_or(0, |c| c.len())
377 }
378
379 pub fn merge(&mut self, other: &HiveDocument) -> bool {
383 let mut changed = false;
384
385 let old_value = self.counter.value();
387 self.counter.merge(&other.counter);
388 if self.counter.value() != old_value {
389 changed = true;
390 }
391
392 if let Some(ref other_emergency) = other.emergency {
394 match &mut self.emergency {
395 Some(ref mut our_emergency) => {
396 if our_emergency.merge(other_emergency) {
397 changed = true;
398 }
399 }
400 None => {
401 self.emergency = Some(other_emergency.clone());
402 changed = true;
403 }
404 }
405 }
406
407 if let Some(ref other_chat) = other.chat {
409 match &mut self.chat {
410 Some(ref mut our_chat) => {
411 if our_chat.merge(other_chat) {
412 changed = true;
413 }
414 }
415 None => {
416 if !other_chat.is_empty() {
417 self.chat = Some(other_chat.clone());
418 changed = true;
419 }
420 }
421 }
422 }
423
424 if changed {
425 self.increment_version();
426 }
427 changed
428 }
429
430 pub fn current_event(&self) -> Option<EventType> {
432 self.peripheral
433 .as_ref()
434 .and_then(|p| p.last_event.as_ref())
435 .map(|e| e.event_type)
436 }
437
438 pub fn encode(&self) -> Vec<u8> {
442 let counter_data = self.counter.encode();
443 let peripheral_data = self.peripheral.as_ref().map(|p| p.encode());
444 let emergency_data = self.emergency.as_ref().map(|e| e.encode());
445 let chat_data = self
446 .chat
447 .as_ref()
448 .filter(|c| !c.is_empty())
449 .map(|c| c.encode());
450
451 let mut size = 8 + counter_data.len(); if let Some(ref pdata) = peripheral_data {
454 size += 4 + pdata.len(); }
456 if let Some(ref edata) = emergency_data {
457 size += 4 + edata.len(); }
459 if let Some(ref cdata) = chat_data {
460 size += 4 + cdata.len(); }
462
463 let mut buf = Vec::with_capacity(size);
464
465 buf.extend_from_slice(&self.version.to_le_bytes());
467 buf.extend_from_slice(&self.node_id.as_u32().to_le_bytes());
468
469 buf.extend_from_slice(&counter_data);
471
472 if let Some(pdata) = peripheral_data {
474 buf.push(EXTENDED_MARKER);
475 buf.push(0); buf.extend_from_slice(&(pdata.len() as u16).to_le_bytes());
477 buf.extend_from_slice(&pdata);
478 }
479
480 if let Some(edata) = emergency_data {
482 buf.push(EMERGENCY_MARKER);
483 buf.push(0); buf.extend_from_slice(&(edata.len() as u16).to_le_bytes());
485 buf.extend_from_slice(&edata);
486 }
487
488 if let Some(cdata) = chat_data {
490 buf.push(CHAT_MARKER);
491 buf.push(0); buf.extend_from_slice(&(cdata.len() as u16).to_le_bytes());
493 buf.extend_from_slice(&cdata);
494 }
495
496 buf
497 }
498
499 #[inline]
504 pub fn to_bytes(&self) -> Vec<u8> {
505 self.encode()
506 }
507
508 pub fn decode(data: &[u8]) -> Option<Self> {
512 if data.len() < MIN_DOCUMENT_SIZE {
513 return None;
514 }
515
516 let version = u32::from_le_bytes([data[0], data[1], data[2], data[3]]);
518 let node_id = NodeId::new(u32::from_le_bytes([data[4], data[5], data[6], data[7]]));
519
520 let counter = GCounter::decode(&data[8..])?;
522
523 let num_entries = u32::from_le_bytes([data[8], data[9], data[10], data[11]]) as usize;
525 let mut offset = 8 + 4 + num_entries * 12;
526
527 let mut peripheral = None;
528 let mut emergency = None;
529 let mut chat = None;
530
531 while offset < data.len() {
533 let marker = data[offset];
534
535 if marker == EXTENDED_MARKER {
536 if data.len() < offset + 4 {
538 break;
539 }
540 let _reserved = data[offset + 1];
541 let section_len = u16::from_le_bytes([data[offset + 2], data[offset + 3]]) as usize;
542
543 let section_start = offset + 4;
544 if data.len() < section_start + section_len {
545 break;
546 }
547
548 peripheral = Peripheral::decode(&data[section_start..section_start + section_len]);
549 offset = section_start + section_len;
550 } else if marker == EMERGENCY_MARKER {
551 if data.len() < offset + 4 {
553 break;
554 }
555 let _reserved = data[offset + 1];
556 let section_len = u16::from_le_bytes([data[offset + 2], data[offset + 3]]) as usize;
557
558 let section_start = offset + 4;
559 if data.len() < section_start + section_len {
560 break;
561 }
562
563 emergency =
564 EmergencyEvent::decode(&data[section_start..section_start + section_len]);
565 offset = section_start + section_len;
566 } else if marker == CHAT_MARKER {
567 if data.len() < offset + 4 {
569 break;
570 }
571 let _reserved = data[offset + 1];
572 let section_len = u16::from_le_bytes([data[offset + 2], data[offset + 3]]) as usize;
573
574 let section_start = offset + 4;
575 if data.len() < section_start + section_len {
576 break;
577 }
578
579 chat = ChatCRDT::decode(&data[section_start..section_start + section_len]);
580 offset = section_start + section_len;
581 } else {
582 break;
584 }
585 }
586
587 Some(Self {
588 version,
589 node_id,
590 counter,
591 peripheral,
592 emergency,
593 chat,
594 })
595 }
596
597 #[inline]
602 pub fn from_bytes(data: &[u8]) -> Option<Self> {
603 Self::decode(data)
604 }
605
606 pub fn total_count(&self) -> u64 {
608 self.counter.value()
609 }
610
611 pub fn encoded_size(&self) -> usize {
615 let counter_size = 4 + self.counter.node_count_total() * 12;
616 let peripheral_size = self.peripheral.as_ref().map_or(0, |p| 4 + p.encode().len());
617 let emergency_size = self.emergency.as_ref().map_or(0, |e| 4 + e.encode().len());
618 let chat_size = self
619 .chat
620 .as_ref()
621 .filter(|c| !c.is_empty())
622 .map_or(0, |c| 4 + c.encoded_size());
623 8 + counter_size + peripheral_size + emergency_size + chat_size
624 }
625
626 pub fn exceeds_target_size(&self) -> bool {
630 self.encoded_size() > TARGET_DOCUMENT_SIZE
631 }
632
633 pub fn exceeds_max_size(&self) -> bool {
637 self.encoded_size() > MAX_DOCUMENT_SIZE
638 }
639}
640
641#[derive(Debug, Clone)]
643pub struct MergeResult {
644 pub source_node: NodeId,
646
647 pub event: Option<PeripheralEvent>,
649
650 pub counter_changed: bool,
652
653 pub emergency_changed: bool,
655
656 pub chat_changed: bool,
658
659 pub total_count: u64,
661}
662
663impl MergeResult {
664 pub fn is_emergency(&self) -> bool {
666 self.event
667 .as_ref()
668 .is_some_and(|e| e.event_type == EventType::Emergency)
669 }
670
671 pub fn is_ack(&self) -> bool {
673 self.event
674 .as_ref()
675 .is_some_and(|e| e.event_type == EventType::Ack)
676 }
677}
678
679#[cfg(test)]
680mod tests {
681 use super::*;
682 use crate::sync::crdt::PeripheralType;
683
684 #[test]
685 fn test_document_encode_decode_minimal() {
686 let node_id = NodeId::new(0x12345678);
687 let doc = HiveDocument::new(node_id);
688
689 let encoded = doc.encode();
690 assert_eq!(encoded.len(), 12); let decoded = HiveDocument::decode(&encoded).unwrap();
693 assert_eq!(decoded.version, 1);
694 assert_eq!(decoded.node_id.as_u32(), 0x12345678);
695 assert_eq!(decoded.counter.value(), 0);
696 assert!(decoded.peripheral.is_none());
697 }
698
699 #[test]
700 fn test_document_encode_decode_with_counter() {
701 let node_id = NodeId::new(0x12345678);
702 let mut doc = HiveDocument::new(node_id);
703 doc.increment_counter();
704 doc.increment_counter();
705
706 let encoded = doc.encode();
707 assert_eq!(encoded.len(), 24);
709
710 let decoded = HiveDocument::decode(&encoded).unwrap();
711 assert_eq!(decoded.counter.value(), 2);
712 }
713
714 #[test]
715 fn test_document_encode_decode_with_peripheral() {
716 let node_id = NodeId::new(0x12345678);
717 let peripheral =
718 Peripheral::new(0xAABBCCDD, PeripheralType::SoldierSensor).with_callsign("ALPHA-1");
719
720 let doc = HiveDocument::new(node_id).with_peripheral(peripheral);
721
722 let encoded = doc.encode();
723 let decoded = HiveDocument::decode(&encoded).unwrap();
724
725 assert!(decoded.peripheral.is_some());
726 let p = decoded.peripheral.unwrap();
727 assert_eq!(p.id, 0xAABBCCDD);
728 assert_eq!(p.callsign_str(), "ALPHA-1");
729 }
730
731 #[test]
732 fn test_document_encode_decode_with_event() {
733 let node_id = NodeId::new(0x12345678);
734 let mut peripheral = Peripheral::new(0xAABBCCDD, PeripheralType::SoldierSensor);
735 peripheral.set_event(EventType::Emergency, TEST_TIMESTAMP);
736
737 let doc = HiveDocument::new(node_id).with_peripheral(peripheral);
738
739 let encoded = doc.encode();
740 let decoded = HiveDocument::decode(&encoded).unwrap();
741
742 assert!(decoded.peripheral.is_some());
743 let p = decoded.peripheral.unwrap();
744 assert!(p.last_event.is_some());
745 let event = p.last_event.unwrap();
746 assert_eq!(event.event_type, EventType::Emergency);
747 assert_eq!(event.timestamp, TEST_TIMESTAMP);
748 }
749
750 #[test]
751 fn test_document_merge() {
752 let node1 = NodeId::new(0x11111111);
753 let node2 = NodeId::new(0x22222222);
754
755 let mut doc1 = HiveDocument::new(node1);
756 doc1.increment_counter();
757
758 let mut doc2 = HiveDocument::new(node2);
759 doc2.counter.increment(&node2, 3);
760
761 let changed = doc1.merge(&doc2);
763 assert!(changed);
764 assert_eq!(doc1.counter.value(), 4); }
766
767 #[test]
768 fn test_merge_result_helpers() {
769 let emergency_event = PeripheralEvent::new(EventType::Emergency, 123);
770 let result = MergeResult {
771 source_node: NodeId::new(0x12345678),
772 event: Some(emergency_event),
773 counter_changed: true,
774 emergency_changed: false,
775 chat_changed: false,
776 total_count: 10,
777 };
778
779 assert!(result.is_emergency());
780 assert!(!result.is_ack());
781
782 let ack_event = PeripheralEvent::new(EventType::Ack, 456);
783 let result = MergeResult {
784 source_node: NodeId::new(0x12345678),
785 event: Some(ack_event),
786 counter_changed: false,
787 emergency_changed: false,
788 chat_changed: false,
789 total_count: 10,
790 };
791
792 assert!(!result.is_emergency());
793 assert!(result.is_ack());
794 }
795
796 #[test]
797 fn test_document_size_calculation() {
798 use crate::sync::crdt::PeripheralType;
799
800 let node_id = NodeId::new(0x12345678);
801
802 let doc = HiveDocument::new(node_id);
804 assert_eq!(doc.encoded_size(), 12);
805 assert!(!doc.exceeds_target_size());
806
807 let mut doc = HiveDocument::new(node_id);
809 doc.increment_counter();
810 assert_eq!(doc.encoded_size(), 24);
811
812 let peripheral = Peripheral::new(0xAABBCCDD, PeripheralType::SoldierSensor);
814 let doc = HiveDocument::new(node_id).with_peripheral(peripheral);
815 let encoded = doc.encode();
816 assert_eq!(doc.encoded_size(), encoded.len());
817
818 let mut doc = HiveDocument::new(node_id);
820 for i in 0..10 {
821 doc.counter.increment(&NodeId::new(i), 1);
822 }
823 assert!(doc.encoded_size() < TARGET_DOCUMENT_SIZE);
824 assert!(!doc.exceeds_max_size());
825 }
826
827 const TEST_TIMESTAMP: u64 = 1705276800000;
833
834 #[test]
835 fn test_document_add_chat_message() {
836 let node_id = NodeId::new(0x12345678);
837 let mut doc = HiveDocument::new(node_id);
838
839 assert!(!doc.has_chat());
840 assert_eq!(doc.chat_count(), 0);
841
842 assert!(doc.add_chat_message(0x12345678, TEST_TIMESTAMP, "ALPHA", "Hello mesh!"));
844 assert!(doc.has_chat());
845 assert_eq!(doc.chat_count(), 1);
846
847 assert!(!doc.add_chat_message(0x12345678, TEST_TIMESTAMP, "ALPHA", "Hello mesh!"));
849 assert_eq!(doc.chat_count(), 1);
850
851 assert!(doc.add_chat_message(0x12345678, TEST_TIMESTAMP + 1000, "ALPHA", "Second message"));
853 assert_eq!(doc.chat_count(), 2);
854 }
855
856 #[test]
857 fn test_document_add_chat_reply() {
858 let node_id = NodeId::new(0x12345678);
859 let mut doc = HiveDocument::new(node_id);
860
861 doc.add_chat_message(0xAABBCCDD, TEST_TIMESTAMP, "BRAVO", "Need assistance");
863
864 assert!(doc.add_chat_reply(
866 0x12345678,
867 TEST_TIMESTAMP + 1000,
868 "ALPHA",
869 "Copy that",
870 0xAABBCCDD, TEST_TIMESTAMP ));
873
874 assert_eq!(doc.chat_count(), 2);
875
876 let chat = doc.get_chat().unwrap();
878 let reply = chat.get_message(0x12345678, TEST_TIMESTAMP + 1000).unwrap();
879 assert!(reply.is_reply());
880 assert_eq!(reply.reply_to_node, 0xAABBCCDD);
881 assert_eq!(reply.reply_to_timestamp, TEST_TIMESTAMP);
882 }
883
884 #[test]
885 fn test_document_encode_decode_with_chat() {
886 let node_id = NodeId::new(0x12345678);
887 let mut doc = HiveDocument::new(node_id);
888
889 doc.add_chat_message(0x12345678, TEST_TIMESTAMP, "ALPHA", "First message");
890 doc.add_chat_message(0xAABBCCDD, TEST_TIMESTAMP + 1000, "BRAVO", "Second message");
891
892 let encoded = doc.encode();
893 let decoded = HiveDocument::decode(&encoded).unwrap();
894
895 assert!(decoded.has_chat());
896 assert_eq!(decoded.chat_count(), 2);
897
898 let chat = decoded.get_chat().unwrap();
899 let msg1 = chat.get_message(0x12345678, TEST_TIMESTAMP).unwrap();
900 assert_eq!(msg1.sender(), "ALPHA");
901 assert_eq!(msg1.text(), "First message");
902
903 let msg2 = chat.get_message(0xAABBCCDD, TEST_TIMESTAMP + 1000).unwrap();
904 assert_eq!(msg2.sender(), "BRAVO");
905 assert_eq!(msg2.text(), "Second message");
906 }
907
908 #[test]
909 fn test_document_merge_with_chat() {
910 let node1 = NodeId::new(0x11111111);
911 let node2 = NodeId::new(0x22222222);
912
913 let mut doc1 = HiveDocument::new(node1);
914 doc1.add_chat_message(0x11111111, TEST_TIMESTAMP, "ALPHA", "From node 1");
915
916 let mut doc2 = HiveDocument::new(node2);
917 doc2.add_chat_message(0x22222222, TEST_TIMESTAMP + 1000, "BRAVO", "From node 2");
918
919 let changed = doc1.merge(&doc2);
921 assert!(changed);
922 assert_eq!(doc1.chat_count(), 2);
923
924 let changed = doc1.merge(&doc2);
926 assert!(!changed);
927
928 let chat = doc1.get_chat().unwrap();
930 assert!(chat.get_message(0x11111111, TEST_TIMESTAMP).is_some());
931 assert!(chat
932 .get_message(0x22222222, TEST_TIMESTAMP + 1000)
933 .is_some());
934 }
935
936 #[test]
937 fn test_document_chat_encoded_size() {
938 let node_id = NodeId::new(0x12345678);
939 let mut doc = HiveDocument::new(node_id);
940
941 let base_size = doc.encoded_size();
942
943 doc.add_chat_message(0x12345678, TEST_TIMESTAMP, "ALPHA", "Test");
945
946 let with_chat_size = doc.encoded_size();
948 assert!(with_chat_size > base_size);
949
950 let encoded = doc.encode();
952 assert_eq!(doc.encoded_size(), encoded.len());
953 }
954}