feagi_agent/sdk/common/
connection_id.rs1use serde::{Deserialize, Serialize};
4use std::fmt;
5
6pub const CONNECTION_ID_LENGTH: usize = 32;
8
9#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
16pub struct ConnectionId {
17 value: [u8; CONNECTION_ID_LENGTH],
18}
19
20impl ConnectionId {
21 pub fn new(value: [u8; CONNECTION_ID_LENGTH]) -> Self {
23 Self { value }
24 }
25
26 pub fn generate() -> Self {
28 use std::time::{SystemTime, UNIX_EPOCH};
29
30 let mut value = [0u8; CONNECTION_ID_LENGTH];
31
32 let timestamp = SystemTime::now()
34 .duration_since(UNIX_EPOCH)
35 .unwrap_or_default()
36 .as_nanos() as u64;
37 value[0..8].copy_from_slice(×tamp.to_le_bytes());
38
39 let ptr = &value as *const _ as u64;
41 value[8..16].copy_from_slice(&ptr.to_le_bytes());
42
43 let entropy = timestamp.wrapping_mul(ptr).wrapping_add(0x517cc1b727220a95);
45 for (i, chunk) in value[16..].chunks_mut(8).enumerate() {
46 let mixed = entropy.wrapping_mul((i + 1) as u64);
47 let bytes = mixed.to_le_bytes();
48 for (j, byte) in chunk.iter_mut().enumerate() {
49 if j < bytes.len() {
50 *byte = bytes[j];
51 }
52 }
53 }
54
55 Self { value }
56 }
57
58 pub fn from_hex(hex: &str) -> Option<Self> {
63 if hex.len() != CONNECTION_ID_LENGTH * 2 {
64 return None;
65 }
66
67 let mut value = [0u8; CONNECTION_ID_LENGTH];
68 for (i, chunk) in hex.as_bytes().chunks(2).enumerate() {
69 let hex_byte = std::str::from_utf8(chunk).ok()?;
70 value[i] = u8::from_str_radix(hex_byte, 16).ok()?;
71 }
72 Some(Self { value })
73 }
74
75 pub fn from_base64(b64: &str) -> Option<Self> {
80 use base64::Engine;
81 let decoded = base64::engine::general_purpose::STANDARD.decode(b64).ok()?;
82 if decoded.len() != CONNECTION_ID_LENGTH {
83 return None;
84 }
85 let mut value = [0u8; CONNECTION_ID_LENGTH];
86 value.copy_from_slice(&decoded);
87 Some(Self { value })
88 }
89
90 pub fn as_bytes(&self) -> &[u8; CONNECTION_ID_LENGTH] {
92 &self.value
93 }
94
95 pub fn to_hex(&self) -> String {
97 self.value.iter().map(|b| format!("{:02x}", b)).collect()
98 }
99
100 pub fn to_base64(&self) -> String {
102 use base64::Engine;
103 base64::engine::general_purpose::STANDARD.encode(self.value)
104 }
105}
106
107impl fmt::Debug for ConnectionId {
109 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
110 f.debug_struct("ConnectionId")
111 .field("value", &self.to_string())
112 .finish()
113 }
114}
115
116impl fmt::Display for ConnectionId {
118 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
119 let hex = self.to_hex();
120 write!(f, "{}...{}", &hex[..4], &hex[hex.len() - 4..])
121 }
122}