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 peer_peripheral: Option<Peripheral>,
655
656 pub counter_changed: bool,
658
659 pub emergency_changed: bool,
661
662 pub chat_changed: bool,
664
665 pub total_count: u64,
667}
668
669impl MergeResult {
670 pub fn is_emergency(&self) -> bool {
672 self.event
673 .as_ref()
674 .is_some_and(|e| e.event_type == EventType::Emergency)
675 }
676
677 pub fn is_ack(&self) -> bool {
679 self.event
680 .as_ref()
681 .is_some_and(|e| e.event_type == EventType::Ack)
682 }
683}
684
685#[cfg(test)]
686mod tests {
687 use super::*;
688 use crate::sync::crdt::PeripheralType;
689
690 #[test]
691 fn test_document_encode_decode_minimal() {
692 let node_id = NodeId::new(0x12345678);
693 let doc = HiveDocument::new(node_id);
694
695 let encoded = doc.encode();
696 assert_eq!(encoded.len(), 12); let decoded = HiveDocument::decode(&encoded).unwrap();
699 assert_eq!(decoded.version, 1);
700 assert_eq!(decoded.node_id.as_u32(), 0x12345678);
701 assert_eq!(decoded.counter.value(), 0);
702 assert!(decoded.peripheral.is_none());
703 }
704
705 #[test]
706 fn test_document_encode_decode_with_counter() {
707 let node_id = NodeId::new(0x12345678);
708 let mut doc = HiveDocument::new(node_id);
709 doc.increment_counter();
710 doc.increment_counter();
711
712 let encoded = doc.encode();
713 assert_eq!(encoded.len(), 24);
715
716 let decoded = HiveDocument::decode(&encoded).unwrap();
717 assert_eq!(decoded.counter.value(), 2);
718 }
719
720 #[test]
721 fn test_document_encode_decode_with_peripheral() {
722 let node_id = NodeId::new(0x12345678);
723 let peripheral =
724 Peripheral::new(0xAABBCCDD, PeripheralType::SoldierSensor).with_callsign("ALPHA-1");
725
726 let doc = HiveDocument::new(node_id).with_peripheral(peripheral);
727
728 let encoded = doc.encode();
729 let decoded = HiveDocument::decode(&encoded).unwrap();
730
731 assert!(decoded.peripheral.is_some());
732 let p = decoded.peripheral.unwrap();
733 assert_eq!(p.id, 0xAABBCCDD);
734 assert_eq!(p.callsign_str(), "ALPHA-1");
735 }
736
737 #[test]
738 fn test_document_encode_decode_with_event() {
739 let node_id = NodeId::new(0x12345678);
740 let mut peripheral = Peripheral::new(0xAABBCCDD, PeripheralType::SoldierSensor);
741 peripheral.set_event(EventType::Emergency, TEST_TIMESTAMP);
742
743 let doc = HiveDocument::new(node_id).with_peripheral(peripheral);
744
745 let encoded = doc.encode();
746 let decoded = HiveDocument::decode(&encoded).unwrap();
747
748 assert!(decoded.peripheral.is_some());
749 let p = decoded.peripheral.unwrap();
750 assert!(p.last_event.is_some());
751 let event = p.last_event.unwrap();
752 assert_eq!(event.event_type, EventType::Emergency);
753 assert_eq!(event.timestamp, TEST_TIMESTAMP);
754 }
755
756 #[test]
757 fn test_document_merge() {
758 let node1 = NodeId::new(0x11111111);
759 let node2 = NodeId::new(0x22222222);
760
761 let mut doc1 = HiveDocument::new(node1);
762 doc1.increment_counter();
763
764 let mut doc2 = HiveDocument::new(node2);
765 doc2.counter.increment(&node2, 3);
766
767 let changed = doc1.merge(&doc2);
769 assert!(changed);
770 assert_eq!(doc1.counter.value(), 4); }
772
773 #[test]
774 fn test_merge_result_helpers() {
775 let emergency_event = PeripheralEvent::new(EventType::Emergency, 123);
776 let result = MergeResult {
777 source_node: NodeId::new(0x12345678),
778 event: Some(emergency_event),
779 peer_peripheral: None,
780 counter_changed: true,
781 emergency_changed: false,
782 chat_changed: false,
783 total_count: 10,
784 };
785
786 assert!(result.is_emergency());
787 assert!(!result.is_ack());
788
789 let ack_event = PeripheralEvent::new(EventType::Ack, 456);
790 let result = MergeResult {
791 source_node: NodeId::new(0x12345678),
792 event: Some(ack_event),
793 peer_peripheral: None,
794 counter_changed: false,
795 emergency_changed: false,
796 chat_changed: false,
797 total_count: 10,
798 };
799
800 assert!(!result.is_emergency());
801 assert!(result.is_ack());
802 }
803
804 #[test]
805 fn test_document_size_calculation() {
806 use crate::sync::crdt::PeripheralType;
807
808 let node_id = NodeId::new(0x12345678);
809
810 let doc = HiveDocument::new(node_id);
812 assert_eq!(doc.encoded_size(), 12);
813 assert!(!doc.exceeds_target_size());
814
815 let mut doc = HiveDocument::new(node_id);
817 doc.increment_counter();
818 assert_eq!(doc.encoded_size(), 24);
819
820 let peripheral = Peripheral::new(0xAABBCCDD, PeripheralType::SoldierSensor);
822 let doc = HiveDocument::new(node_id).with_peripheral(peripheral);
823 let encoded = doc.encode();
824 assert_eq!(doc.encoded_size(), encoded.len());
825
826 let mut doc = HiveDocument::new(node_id);
828 for i in 0..10 {
829 doc.counter.increment(&NodeId::new(i), 1);
830 }
831 assert!(doc.encoded_size() < TARGET_DOCUMENT_SIZE);
832 assert!(!doc.exceeds_max_size());
833 }
834
835 const TEST_TIMESTAMP: u64 = 1705276800000;
841
842 #[test]
843 fn test_document_add_chat_message() {
844 let node_id = NodeId::new(0x12345678);
845 let mut doc = HiveDocument::new(node_id);
846
847 assert!(!doc.has_chat());
848 assert_eq!(doc.chat_count(), 0);
849
850 assert!(doc.add_chat_message(0x12345678, TEST_TIMESTAMP, "ALPHA", "Hello mesh!"));
852 assert!(doc.has_chat());
853 assert_eq!(doc.chat_count(), 1);
854
855 assert!(!doc.add_chat_message(0x12345678, TEST_TIMESTAMP, "ALPHA", "Hello mesh!"));
857 assert_eq!(doc.chat_count(), 1);
858
859 assert!(doc.add_chat_message(0x12345678, TEST_TIMESTAMP + 1000, "ALPHA", "Second message"));
861 assert_eq!(doc.chat_count(), 2);
862 }
863
864 #[test]
865 fn test_document_add_chat_reply() {
866 let node_id = NodeId::new(0x12345678);
867 let mut doc = HiveDocument::new(node_id);
868
869 doc.add_chat_message(0xAABBCCDD, TEST_TIMESTAMP, "BRAVO", "Need assistance");
871
872 assert!(doc.add_chat_reply(
874 0x12345678,
875 TEST_TIMESTAMP + 1000,
876 "ALPHA",
877 "Copy that",
878 0xAABBCCDD, TEST_TIMESTAMP ));
881
882 assert_eq!(doc.chat_count(), 2);
883
884 let chat = doc.get_chat().unwrap();
886 let reply = chat.get_message(0x12345678, TEST_TIMESTAMP + 1000).unwrap();
887 assert!(reply.is_reply());
888 assert_eq!(reply.reply_to_node, 0xAABBCCDD);
889 assert_eq!(reply.reply_to_timestamp, TEST_TIMESTAMP);
890 }
891
892 #[test]
893 fn test_document_encode_decode_with_chat() {
894 let node_id = NodeId::new(0x12345678);
895 let mut doc = HiveDocument::new(node_id);
896
897 doc.add_chat_message(0x12345678, TEST_TIMESTAMP, "ALPHA", "First message");
898 doc.add_chat_message(0xAABBCCDD, TEST_TIMESTAMP + 1000, "BRAVO", "Second message");
899
900 let encoded = doc.encode();
901 let decoded = HiveDocument::decode(&encoded).unwrap();
902
903 assert!(decoded.has_chat());
904 assert_eq!(decoded.chat_count(), 2);
905
906 let chat = decoded.get_chat().unwrap();
907 let msg1 = chat.get_message(0x12345678, TEST_TIMESTAMP).unwrap();
908 assert_eq!(msg1.sender(), "ALPHA");
909 assert_eq!(msg1.text(), "First message");
910
911 let msg2 = chat.get_message(0xAABBCCDD, TEST_TIMESTAMP + 1000).unwrap();
912 assert_eq!(msg2.sender(), "BRAVO");
913 assert_eq!(msg2.text(), "Second message");
914 }
915
916 #[test]
917 fn test_document_merge_with_chat() {
918 let node1 = NodeId::new(0x11111111);
919 let node2 = NodeId::new(0x22222222);
920
921 let mut doc1 = HiveDocument::new(node1);
922 doc1.add_chat_message(0x11111111, TEST_TIMESTAMP, "ALPHA", "From node 1");
923
924 let mut doc2 = HiveDocument::new(node2);
925 doc2.add_chat_message(0x22222222, TEST_TIMESTAMP + 1000, "BRAVO", "From node 2");
926
927 let changed = doc1.merge(&doc2);
929 assert!(changed);
930 assert_eq!(doc1.chat_count(), 2);
931
932 let changed = doc1.merge(&doc2);
934 assert!(!changed);
935
936 let chat = doc1.get_chat().unwrap();
938 assert!(chat.get_message(0x11111111, TEST_TIMESTAMP).is_some());
939 assert!(chat
940 .get_message(0x22222222, TEST_TIMESTAMP + 1000)
941 .is_some());
942 }
943
944 #[test]
945 fn test_document_chat_encoded_size() {
946 let node_id = NodeId::new(0x12345678);
947 let mut doc = HiveDocument::new(node_id);
948
949 let base_size = doc.encoded_size();
950
951 doc.add_chat_message(0x12345678, TEST_TIMESTAMP, "ALPHA", "Test");
953
954 let with_chat_size = doc.encoded_size();
956 assert!(with_chat_size > base_size);
957
958 let encoded = doc.encode();
960 assert_eq!(doc.encoded_size(), encoded.len());
961 }
962}