1use core::fmt;
6use std::fmt::Display;
7
8use borderless_hash::Hash256;
9use bytes::{Buf, BufMut, Bytes, BytesMut};
10use serde::{Deserialize, Serialize};
11
12pub use uuid::Uuid;
13
14macro_rules! impl_uuid {
16 ($type:ident, $prefix_upper:literal, $prefix_lower:literal) => {
17 impl $type {
18 #[cfg(any(all(feature = "generate_ids", not(target_arch = "wasm32")), test))]
20 pub fn generate() -> Self {
21 let uuid = uuid::Uuid::new_v4();
23 uuid.into()
25 }
26
27 pub fn into_uuid(self) -> uuid::Uuid {
29 self.0
30 }
31
32 pub fn from_bytes(mut bytes: [u8; 16]) -> Self {
36 bytes[0] = bytes[0] & $prefix_upper | $prefix_lower;
37 $type(uuid::Uuid::new_v8(bytes))
38 }
39
40 pub fn from_slice(slice: &[u8]) -> Result<Self, std::array::TryFromSliceError> {
41 slice.try_into()
42 }
43
44 pub fn into_bytes(self) -> [u8; 16] {
46 self.0.into_bytes()
47 }
48
49 pub fn as_bytes(&self) -> &[u8; 16] {
51 self.0.as_bytes()
52 }
53
54 pub fn parse_str(s: &str) -> Result<Self, uuid::Error> {
56 let uuid = Uuid::parse_str(s)?;
57 Ok(uuid.into())
58 }
59
60 pub fn merge(&self, other: &Self) -> [u8; 16] {
64 let mut out = [0; 16];
65 for i in 0..16 {
66 out[i] = self.as_bytes()[i] ^ other.as_bytes()[i];
67 }
68 out
69 }
70
71 pub fn merge_compact(&self, other: &Self) -> u64 {
75 let merged = self.merge(other);
76 let mut out = [0; 8];
77 for i in 0..8 {
78 out[i] = merged[i] ^ merged[i + 8];
79 }
80 u64::from_be_bytes(out)
81 }
82
83 pub fn compact(&self) -> u64 {
87 let mut out = [0; 8];
88 for i in 0..8 {
89 out[i] = self.as_bytes()[i] ^ self.as_bytes()[i + 8];
90 }
91 u64::from_be_bytes(out)
92 }
93 }
94
95 impl<'de> serde::Deserialize<'de> for $type {
96 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
97 where
98 D: serde::Deserializer<'de>,
99 {
100 let uuid = uuid::Uuid::deserialize(deserializer)?;
102 Ok($type::from(uuid))
103 }
104 }
105
106 impl From<u128> for $type {
107 fn from(value: u128) -> Self {
108 let mut bytes = value.to_be_bytes();
109 bytes[0] = bytes[0] & $prefix_upper | $prefix_lower;
110 $type(uuid::Uuid::new_v8(bytes))
111 }
112 }
113
114 impl From<uuid::Uuid> for $type {
115 fn from(value: uuid::Uuid) -> Self {
116 let mut bytes = value.into_bytes();
117 bytes[0] = bytes[0] & $prefix_upper | $prefix_lower;
118 $type(uuid::Uuid::new_v8(bytes))
119 }
120 }
121
122 impl From<$type> for uuid::Uuid {
123 fn from(value: $type) -> uuid::Uuid {
124 value.0
125 }
126 }
127
128 impl From<$type> for u128 {
129 fn from(value: $type) -> u128 {
130 value.0.as_u128()
131 }
132 }
133
134 impl TryFrom<&str> for $type {
135 type Error = uuid::Error;
136
137 fn try_from(value: &str) -> Result<Self, Self::Error> {
138 uuid::Uuid::parse_str(&value).map(Into::into)
139 }
140 }
141
142 impl TryFrom<String> for $type {
143 type Error = uuid::Error;
144
145 fn try_from(value: String) -> Result<Self, Self::Error> {
146 $type::try_from(value.as_str())
147 }
148 }
149
150 impl TryFrom<&[u8]> for $type {
151 type Error = std::array::TryFromSliceError;
152
153 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
154 let bytes = value.try_into()?;
155 Ok($type::from_bytes(bytes))
156 }
157 }
158
159 impl std::str::FromStr for $type {
160 type Err = uuid::Error;
161
162 fn from_str(s: &str) -> Result<Self, Self::Err> {
163 uuid::Uuid::parse_str(s).map(Into::into)
164 }
165 }
166
167 impl std::fmt::Display for $type {
168 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
169 write!(f, "{}", self.0)
170 }
171 }
172
173 impl From<$type> for String {
174 fn from(value: $type) -> String {
175 value.0.to_string()
176 }
177 }
178
179 impl From<&$type> for String {
180 fn from(value: &$type) -> String {
181 value.0.to_string()
182 }
183 }
184
185 impl AsRef<[u8]> for $type {
186 fn as_ref(&self) -> &[u8] {
187 self.0.as_bytes()
188 }
189 }
190
191 impl AsRef<[u8; 16]> for $type {
192 fn as_ref(&self) -> &[u8; 16] {
193 self.0.as_bytes()
194 }
195 }
196 };
197}
198
199#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Hash, PartialOrd, Ord)]
216pub struct AgentId(uuid::Uuid);
217impl_uuid!(AgentId, 0xaf, 0xa0);
218
219#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Hash, PartialOrd, Ord)]
236pub struct BorderlessId(uuid::Uuid);
237impl_uuid!(BorderlessId, 0xbf, 0xb0);
238
239#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Hash, PartialOrd, Ord)]
256pub struct ContractId(uuid::Uuid);
257impl_uuid!(ContractId, 0xcf, 0xc0);
258
259#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Hash, PartialOrd, Ord)]
276pub struct Did(uuid::Uuid);
277impl_uuid!(Did, 0xdf, 0xd0);
278
279#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Hash, PartialOrd, Ord)]
296pub struct ExternalId(uuid::Uuid);
297impl_uuid!(ExternalId, 0xef, 0xe0);
298#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Hash, PartialOrd, Ord)]
317pub struct FlowId(uuid::Uuid);
318impl_uuid!(FlowId, 0xff, 0xf0);
319
320pub fn aid_prefix(bytes: impl AsRef<[u8]>) -> bool {
324 let bytes = bytes.as_ref();
325 if bytes.is_empty() {
326 return false;
327 }
328 bytes[0] | 0x0f == 0xaf
329}
330
331pub fn bid_prefix(bytes: impl AsRef<[u8]>) -> bool {
335 let bytes = bytes.as_ref();
336 if bytes.is_empty() {
337 return false;
338 }
339 bytes[0] | 0x0f == 0xbf
340}
341
342pub fn cid_prefix(bytes: impl AsRef<[u8]>) -> bool {
346 let bytes = bytes.as_ref();
347 if bytes.is_empty() {
348 return false;
349 }
350 bytes[0] | 0x0f == 0xcf
351}
352
353pub fn did_prefix(bytes: impl AsRef<[u8]>) -> bool {
357 let bytes = bytes.as_ref();
358 if bytes.is_empty() {
359 return false;
360 }
361 bytes[0] | 0x0f == 0xdf
362}
363
364pub fn eid_prefix(bytes: impl AsRef<[u8]>) -> bool {
368 let bytes = bytes.as_ref();
369 if bytes.is_empty() {
370 return false;
371 }
372 bytes[0] | 0x0f == 0xef
373}
374
375pub fn fid_prefix(bytes: impl AsRef<[u8]>) -> bool {
379 let bytes = bytes.as_ref();
380 if bytes.is_empty() {
381 return false;
382 }
383 bytes[0] | 0x0f == 0xff
384}
385
386#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)]
391pub struct BlockIdentifier {
392 pub chain_id: u32,
394 pub number: u64,
396 pub hash: Hash256,
401}
402
403impl BlockIdentifier {
404 pub fn new(chain_id: u32, number: u64, hash: Hash256) -> Self {
406 Self {
407 chain_id,
408 number,
409 hash,
410 }
411 }
412
413 pub fn genesis(chain_id: u32) -> Self {
414 Self::new(chain_id, 0, Hash256::empty())
415 }
416
417 pub fn from_bytes(bytes: Vec<u8>) -> Result<Self, InvalidBlockIdentifier> {
419 if bytes.len() != ENCODED_BLOCK_ID_LEN {
420 Err(InvalidBlockIdentifier(bytes.len()))
421 } else {
422 let mut bytes = Bytes::from(bytes);
423 let chain_id = bytes.get_u32();
424 let number = bytes.get_u64();
425 let mut hash_slice = [0u8; 32];
426 bytes.copy_to_slice(&mut hash_slice);
427 Ok(BlockIdentifier {
428 chain_id,
429 number,
430 hash: hash_slice.into(),
431 })
432 }
433 }
434
435 pub fn to_bytes(&self) -> Vec<u8> {
437 let mut buf = BytesMut::with_capacity(std::mem::size_of::<BlockIdentifier>());
438 buf.put_u32(self.chain_id);
439 buf.put_u64(self.number);
440 buf.put_slice(self.hash.as_ref());
441 buf.into()
442 }
443}
444
445const ENCODED_BLOCK_ID_LEN: usize = 4 + 8 + 32;
451
452#[derive(Debug, PartialEq)]
453pub struct InvalidBlockIdentifier(pub usize);
454impl Display for InvalidBlockIdentifier {
455 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
456 write!(
457 f,
458 "failed to decode block identifier - expected {ENCODED_BLOCK_ID_LEN} bytes, got {}",
459 self.0
460 )
461 }
462}
463impl std::error::Error for InvalidBlockIdentifier {}
464
465impl PartialOrd for BlockIdentifier {
469 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
470 Some(self.cmp(other))
471 }
472}
473
474impl Ord for BlockIdentifier {
475 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
476 self.hash.cmp(&other.hash)
477 }
478}
479
480impl Display for BlockIdentifier {
481 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
482 write!(f, "{}.{}.{}", self.chain_id, self.number, self.hash)
483 }
484}
485
486#[allow(dead_code)]
491#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
492pub struct TxIdentifier {
493 pub chain_id: u32,
495 pub number: u64,
497 pub hash: Hash256,
499}
500
501impl TxIdentifier {
502 pub fn new(chain_id: u32, number: u64, hash: Hash256) -> Self {
504 TxIdentifier {
505 chain_id,
506 number,
507 hash,
508 }
509 }
510
511 pub fn from_bytes(bytes: Vec<u8>) -> Result<Self, InvalidTxIdentifier> {
513 if bytes.len() != ENCODED_TX_ID_LEN {
514 Err(InvalidTxIdentifier(bytes.len()))
515 } else {
516 let mut bytes = Bytes::from(bytes);
517 let chain_id = bytes.get_u32();
518 let number = bytes.get_u64();
519 let mut hash_slice = [0u8; 32];
520 bytes.copy_to_slice(&mut hash_slice);
521 Ok(TxIdentifier {
522 chain_id,
523 number,
524 hash: hash_slice.into(),
525 })
526 }
527 }
528
529 pub fn to_bytes(&self) -> Vec<u8> {
531 let mut buf = BytesMut::with_capacity(std::mem::size_of::<TxIdentifier>());
532 buf.put_u32(self.chain_id);
533 buf.put_u64(self.number);
534 buf.put_slice(self.hash.as_ref());
535 buf.into()
536 }
537}
538
539const ENCODED_TX_ID_LEN: usize = 4 + 8 + 32;
545
546#[derive(Debug, PartialEq)]
547pub struct InvalidTxIdentifier(pub usize);
548impl Display for InvalidTxIdentifier {
549 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
550 write!(
551 f,
552 "failed to decode transaction identifier - expected {ENCODED_TX_ID_LEN} bytes, got {}",
553 self.0
554 )
555 }
556}
557impl std::error::Error for InvalidTxIdentifier {}
558
559impl Display for TxIdentifier {
560 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
561 write!(f, "{}.{}.{}", self.chain_id, self.number, self.hash)
562 }
563}
564
565#[cfg(test)]
566mod tests {
567 use super::*;
568 use serde::de::DeserializeOwned;
569 use uuid::Uuid;
570
571 #[test]
572 fn tx_id_encode_decode() {
573 let tx_id = TxIdentifier::new(1, 2, Hash256::empty());
574 let bytes = tx_id.to_bytes();
575 let result = TxIdentifier::from_bytes(bytes);
576 assert!(result.is_ok(), "decoding failed: {}", result.unwrap_err());
577 assert_eq!(result.unwrap(), tx_id);
578 }
579
580 #[test]
581 fn tx_id_display() {
582 let tx_id = TxIdentifier::new(1, 2, Hash256::empty());
583 assert_eq!(format!("{tx_id}"), "1.2.a7ffc6f8");
584 }
585
586 #[test]
587 fn block_id_encode_decode() {
588 let block_id = BlockIdentifier::new(1, 2, Hash256::empty());
589 let bytes = block_id.to_bytes();
590 let result = BlockIdentifier::from_bytes(bytes);
591 assert!(result.is_ok(), "decoding failed: {}", result.unwrap_err());
592 assert_eq!(result.unwrap(), block_id);
593 }
594
595 #[test]
596 fn block_id_display() {
597 let block_id = BlockIdentifier::new(1, 2, Hash256::empty());
598 assert_eq!(format!("{block_id}"), "1.2.a7ffc6f8");
599 }
600
601 #[test]
602 fn block_id_ordering() {
603 let first = BlockIdentifier::new(10, 20, Hash256::zero());
605 let last = BlockIdentifier::new(1, 2, Hash256::empty());
606 assert!(first < last);
607
608 for i in 0..1_000u64 {
610 let h1 = Hash256::digest(&i.to_be_bytes());
611 let h2 = Hash256::digest(&i.to_le_bytes());
612 let b1 = BlockIdentifier::new(i as u32, i, h1);
613 let b2 = BlockIdentifier::new(i as u32 + 1, i + 1, h2);
614 assert_eq!(
615 h1 < h2,
616 b1 < b2,
617 "block identifiers must be sorted based on their hash"
618 );
619 }
620 }
621
622 #[test]
623 fn agent_id_prefix() {
624 for _ in 0..1_000_000 {
625 let base_id = Uuid::new_v4();
626 let process_id = AgentId::from(base_id);
627 let pid_string = process_id.to_string();
628 assert_eq!(
629 pid_string.chars().next(),
630 Some('a'),
631 "Process-IDs must be prefixed with 'a' in string representation"
632 );
633 let back_to_uuid: Uuid = process_id.into();
634 assert_ne!(base_id, back_to_uuid);
635 assert_eq!(back_to_uuid.as_bytes()[0] & 0xF0, 0xa0);
639 }
640 }
641
642 #[test]
643 fn borderless_id_prefix() {
644 for _ in 0..1_000_000 {
645 let base_id = Uuid::new_v4();
646 let borderless_id = BorderlessId::from(base_id);
647 let pid_string = borderless_id.to_string();
648 assert_eq!(
649 pid_string.chars().next(),
650 Some('b'),
651 "Borderless-IDs must be prefixed with 'b' in string representation"
652 );
653 let back_to_uuid: Uuid = borderless_id.into();
654 assert_ne!(base_id, back_to_uuid);
655 assert_eq!(back_to_uuid.as_bytes()[0] & 0xF0, 0xb0);
659 }
660 }
661
662 #[test]
663 fn contract_id_prefix() {
664 for _ in 0..1_000_000 {
665 let base_id = Uuid::new_v4();
666 let contract_id = ContractId::from(base_id);
667 let cid_string = contract_id.to_string();
668 assert_eq!(
669 cid_string.chars().next(),
670 Some('c'),
671 "Contract-IDs must be prefixed with 'c' in string representation"
672 );
673 let back_to_uuid: Uuid = contract_id.into();
674 assert_ne!(base_id, back_to_uuid);
675 assert_eq!(back_to_uuid.as_bytes()[0] & 0xF0, 0xc0);
679 }
680 }
681
682 #[test]
683 fn decentralized_id_prefix() {
684 for _ in 0..1_000_000 {
685 let base_id = Uuid::new_v4();
686 let asset_id = Did::from(base_id);
687 let aid_string = asset_id.to_string();
688 assert_eq!(
689 aid_string.chars().next(),
690 Some('d'),
691 "Decentralized-IDs must be prefixed with 'd' in string representation"
692 );
693 let back_to_uuid: Uuid = asset_id.into();
694 assert_ne!(base_id, back_to_uuid);
695 assert_eq!(back_to_uuid.as_bytes()[0] & 0xF0, 0xd0);
699 }
700 }
701
702 #[test]
703 fn external_id_prefix() {
704 for _ in 0..1_000_000 {
705 let base_id = Uuid::new_v4();
706 let asset_id = ExternalId::from(base_id);
707 let aid_string = asset_id.to_string();
708 assert_eq!(
709 aid_string.chars().next(),
710 Some('e'),
711 "External-IDs must be prefixed with 'e' in string representation"
712 );
713 let back_to_uuid: Uuid = asset_id.into();
714 assert_ne!(base_id, back_to_uuid);
715 assert_eq!(back_to_uuid.as_bytes()[0] & 0xF0, 0xe0);
719 }
720 }
721
722 #[test]
723 fn differentiate_id_types() {
724 for _ in 0..1_000_000 {
728 let base_id = Uuid::new_v4();
729 let agent_id: Uuid = AgentId::from(base_id).into();
730 let borderless_id: Uuid = BorderlessId::from(base_id).into();
731 let contract_id: Uuid = ContractId::from(base_id).into();
732 let did: Uuid = Did::from(base_id).into();
733 let external_id: Uuid = ExternalId::from(base_id).into();
734 let flow_id: Uuid = FlowId::from(base_id).into();
735 let ids = [
736 base_id,
737 agent_id,
738 borderless_id,
739 contract_id,
740 did,
741 external_id,
742 flow_id,
743 ];
744 for i in 0..ids.len() {
746 for j in i..ids.len() {
747 assert_eq!(ids[i] == ids[j], i == j);
748 }
749 }
750 }
751 }
752
753 #[test]
754 fn agent_id_construction() {
755 for _ in 0..1_000_000 {
756 let base_id = Uuid::new_v4();
757 let base_u128 = base_id.as_u128();
758 let from_uuid = AgentId::from(base_id);
759 let from_u128 = AgentId::from(base_u128);
760 assert_eq!(from_uuid, from_u128);
761 let back_to_uuid: Uuid = from_uuid.into();
762 let back_to_u128: u128 = from_u128.into();
763 assert_eq!(back_to_uuid, Uuid::from_u128(back_to_u128));
764 assert_eq!(back_to_uuid.as_u128(), back_to_u128); assert_ne!(base_id, back_to_uuid);
766 assert_ne!(base_id.as_u128(), back_to_u128);
767 }
768 }
769
770 #[test]
771 fn borderless_id_construction() {
772 for _ in 0..1_000_000 {
773 let base_id = Uuid::new_v4();
774 let base_u128 = base_id.as_u128();
775 let from_uuid = BorderlessId::from(base_id);
776 let from_u128 = BorderlessId::from(base_u128);
777 assert_eq!(from_uuid, from_u128);
778 let back_to_uuid: Uuid = from_uuid.into();
779 let back_to_u128: u128 = from_u128.into();
780 assert_eq!(back_to_uuid, Uuid::from_u128(back_to_u128));
781 assert_eq!(back_to_uuid.as_u128(), back_to_u128); assert_ne!(base_id, back_to_uuid);
783 assert_ne!(base_id.as_u128(), back_to_u128);
784 }
785 }
786
787 #[test]
788 fn contract_id_construction() {
789 for _ in 0..1_000_000 {
790 let base_id = Uuid::new_v4();
791 let base_u128 = base_id.as_u128();
792 let from_uuid = ContractId::from(base_id);
793 let from_u128 = ContractId::from(base_u128);
794 assert_eq!(from_uuid, from_u128);
795 let back_to_uuid: Uuid = from_uuid.into();
796 let back_to_u128: u128 = from_u128.into();
797 assert_eq!(back_to_uuid, Uuid::from_u128(back_to_u128));
798 assert_eq!(back_to_uuid.as_u128(), back_to_u128); assert_ne!(base_id, back_to_uuid);
800 assert_ne!(base_id.as_u128(), back_to_u128);
801 }
802 }
803
804 #[test]
805 fn did_construction() {
806 for _ in 0..1_000_000 {
807 let base_id = Uuid::new_v4();
808 let base_u128 = base_id.as_u128();
809 let from_uuid = Did::from(base_id);
810 let from_u128 = Did::from(base_u128);
811 assert_eq!(from_uuid, from_u128);
812 let back_to_uuid: Uuid = from_uuid.into();
813 let back_to_u128: u128 = from_u128.into();
814 assert_eq!(back_to_uuid, Uuid::from_u128(back_to_u128));
815 assert_eq!(back_to_uuid.as_u128(), back_to_u128); assert_ne!(base_id, back_to_uuid);
817 assert_ne!(base_id.as_u128(), back_to_u128);
818 }
819 }
820
821 #[test]
822 fn external_id_construction() {
823 for _ in 0..1_000_000 {
824 let base_id = Uuid::new_v4();
825 let base_u128 = base_id.as_u128();
826 let from_uuid = ExternalId::from(base_id);
827 let from_u128 = ExternalId::from(base_u128);
828 assert_eq!(from_uuid, from_u128);
829 let back_to_uuid: Uuid = from_uuid.into();
830 let back_to_u128: u128 = from_u128.into();
831 assert_eq!(back_to_uuid, Uuid::from_u128(back_to_u128));
832 assert_eq!(back_to_uuid.as_u128(), back_to_u128); assert_ne!(base_id, back_to_uuid);
834 assert_ne!(base_id.as_u128(), back_to_u128);
835 }
836 }
837
838 #[test]
839 fn check_aid_prefix() {
840 for _ in 0..1_000_000 {
841 assert!(aid_prefix(&AgentId::generate()));
842 assert!(!aid_prefix(&BorderlessId::generate()));
843 assert!(!aid_prefix(&ContractId::generate()));
844 assert!(!aid_prefix(&Did::generate()));
845 assert!(!aid_prefix(&ExternalId::generate()));
846 assert!(!aid_prefix(&FlowId::generate()));
847 assert!(!aid_prefix(&[]));
848 }
849 }
850
851 #[test]
852 fn check_bid_prefix() {
853 for _ in 0..1_000_000 {
854 assert!(!bid_prefix(&AgentId::generate()));
855 assert!(bid_prefix(&BorderlessId::generate()));
856 assert!(!bid_prefix(&ContractId::generate()));
857 assert!(!bid_prefix(&Did::generate()));
858 assert!(!bid_prefix(&ExternalId::generate()));
859 assert!(!bid_prefix(&FlowId::generate()));
860 assert!(!bid_prefix(&[]));
861 }
862 }
863
864 #[test]
865 fn check_cid_prefix() {
866 for _ in 0..1_000_000 {
867 assert!(!cid_prefix(&AgentId::generate()));
868 assert!(!cid_prefix(&BorderlessId::generate()));
869 assert!(cid_prefix(&ContractId::generate()));
870 assert!(!cid_prefix(&Did::generate()));
871 assert!(!cid_prefix(&ExternalId::generate()));
872 assert!(!cid_prefix(&FlowId::generate()));
873 assert!(!cid_prefix(&[]));
874 }
875 }
876
877 #[test]
878 fn check_did_prefix() {
879 for _ in 0..1_000_000 {
880 assert!(!did_prefix(&AgentId::generate()));
881 assert!(!did_prefix(&BorderlessId::generate()));
882 assert!(!did_prefix(&ContractId::generate()));
883 assert!(did_prefix(&Did::generate()));
884 assert!(!did_prefix(&ExternalId::generate()));
885 assert!(!did_prefix(&FlowId::generate()));
886 assert!(!did_prefix(&[]));
887 }
888 }
889
890 #[test]
891 fn check_eid_prefix() {
892 for _ in 0..1_000_000 {
893 assert!(!eid_prefix(&AgentId::generate()));
894 assert!(!eid_prefix(&BorderlessId::generate()));
895 assert!(!eid_prefix(&ContractId::generate()));
896 assert!(!eid_prefix(&Did::generate()));
897 assert!(eid_prefix(&ExternalId::generate()));
898 assert!(!eid_prefix(&FlowId::generate()));
899 assert!(!eid_prefix(&[]));
900 }
901 }
902
903 #[test]
904 fn check_fid_prefix() {
905 for _ in 0..1_000_000 {
906 assert!(!fid_prefix(&AgentId::generate()));
907 assert!(!fid_prefix(&BorderlessId::generate()));
908 assert!(!fid_prefix(&ContractId::generate()));
909 assert!(!fid_prefix(&Did::generate()));
910 assert!(!fid_prefix(&ExternalId::generate()));
911 assert!(fid_prefix(&FlowId::generate()));
912 assert!(!fid_prefix(&[]));
913 }
914 }
915
916 #[test]
917 fn string_representation() {
918 fn parse<T>(id: T)
919 where
920 T: Into<String> + TryFrom<String> + fmt::Debug + Eq + Copy,
921 {
922 let s1: String = id.into();
923 let a1: Result<T, _> = s1.try_into();
924 assert!(a1.is_ok());
925 let id2 = unsafe { a1.unwrap_unchecked() };
926 assert_eq!(id, id2);
927 }
928 parse(AgentId::generate());
929 parse(BorderlessId::generate());
930 parse(ContractId::generate());
931 parse(Did::generate());
932 parse(ExternalId::generate());
933 parse(FlowId::generate());
934 }
935
936 #[test]
937 fn uuid_conversion() {
938 let uid = Uuid::new_v4();
939 let aid: AgentId = uid.into();
940 let bid: BorderlessId = uid.into();
941 let cid: ContractId = uid.into();
942 let did: Did = uid.into();
943 let eid: ExternalId = uid.into();
944 let fid: FlowId = uid.into();
945 assert_ne!(aid.into_bytes(), uid.into_bytes());
947 assert_ne!(bid.into_bytes(), uid.into_bytes());
948 assert_ne!(cid.into_bytes(), uid.into_bytes());
949 assert_ne!(did.into_bytes(), uid.into_bytes());
950 assert_ne!(eid.into_bytes(), uid.into_bytes());
951 assert_ne!(fid.into_bytes(), uid.into_bytes());
952 assert_ne!(aid.into_uuid(), uid);
953 assert_ne!(bid.into_uuid(), uid);
954 assert_ne!(cid.into_uuid(), uid);
955 assert_ne!(did.into_uuid(), uid);
956 assert_ne!(eid.into_uuid(), uid);
957 assert_ne!(fid.into_uuid(), uid);
958
959 assert_eq!(AgentId::from(aid.into_uuid()), aid);
961 assert_eq!(BorderlessId::from(aid.into_uuid()), bid);
962 assert_eq!(ContractId::from(aid.into_uuid()), cid);
963 assert_eq!(Did::from(aid.into_uuid()), did);
964 assert_eq!(ExternalId::from(aid.into_uuid()), eid);
965 assert_eq!(FlowId::from(aid.into_uuid()), fid);
966 }
967
968 #[test]
969 fn merge_is_commutative() {
970 for _ in 0..1_000_000 {
971 let bid_1 = BorderlessId::generate();
972 let bid_2 = BorderlessId::generate();
973 assert_eq!(bid_1.merge(&bid_2), bid_2.merge(&bid_1));
974 assert_eq!(bid_1.merge(&bid_1), [0; 16]);
976 }
977 }
978
979 #[test]
980 fn merge_compact_is_commutative() {
981 for _ in 0..1_000_000 {
982 let bid_1 = BorderlessId::generate();
983 let bid_2 = BorderlessId::generate();
984 assert_eq!(bid_1.merge_compact(&bid_2), bid_2.merge_compact(&bid_1));
985 assert_eq!(bid_1.merge_compact(&bid_1), 0);
987 }
988 }
989
990 #[test]
991 fn compact() {
992 for _ in 0..1_000_000 {
993 let bid_1 = BorderlessId::generate();
994 let bid_2 = BorderlessId::generate();
995 assert_ne!(bid_1.compact(), bid_2.compact());
996 assert_ne!(bid_1.compact(), 0);
998 }
999 }
1000
1001 #[test]
1002 fn compact_merge() {
1003 for _ in 0..1_000_000 {
1004 let bid_1 = BorderlessId::generate();
1005 let bid_2 = BorderlessId::generate();
1006 let merge_compact = bid_1.merge_compact(&bid_2);
1007 let merged_bytes = bid_1.merge(&bid_2);
1008 let merged_id = BorderlessId::from_bytes(merged_bytes);
1009 let compacted = merged_id.compact();
1010 assert_eq!(
1013 compacted == merge_compact,
1014 merged_bytes == *merged_id.as_bytes()
1015 );
1016 let mut manual_compact = [0; 8];
1018 for i in 0..8 {
1019 manual_compact[i] = merged_bytes[i] ^ merged_bytes[i + 8];
1020 }
1021 assert_eq!(merge_compact, u64::from_be_bytes(manual_compact));
1022 }
1023 }
1024
1025 #[test]
1026 fn serde_conversion_is_ensured() {
1027 fn check_id<T: DeserializeOwned + From<Uuid> + Into<Uuid> + std::fmt::Debug + Eq>(
1028 base_id: Uuid,
1029 ) {
1030 let json_id = serde_json::to_string(&base_id).unwrap();
1031 let result: Result<T, _> = serde_json::from_str(&json_id);
1032 assert!(result.is_ok(), "{}", result.unwrap_err());
1033 let bid = result.unwrap();
1034 assert_eq!(bid, T::from(base_id));
1035 assert_ne!(bid.into(), base_id);
1036 }
1037 for _ in 0..100_000 {
1038 let base_id = Uuid::new_v4();
1039 check_id::<AgentId>(base_id);
1040 check_id::<BorderlessId>(base_id);
1041 check_id::<ContractId>(base_id);
1042 check_id::<Did>(base_id);
1043 check_id::<ExternalId>(base_id);
1044 check_id::<FlowId>(base_id);
1045 }
1046 }
1047}