cortexfs_channels/
route.rs1use crate::{ChannelError, InboundMessage, MessageTarget};
2
3#[derive(Clone, Debug, Eq, PartialEq)]
5pub struct ChannelSessionRoute {
6 agent: String,
7 prefix: String,
8}
9
10impl ChannelSessionRoute {
11 pub fn new(agent: impl Into<String>, prefix: impl Into<String>) -> Result<Self, ChannelError> {
12 let agent = agent.into();
13 let prefix = prefix.into();
14 if !valid_name(&agent) || !valid_name(&prefix) {
15 return Err(ChannelError::InvalidValue(format!("{agent}/{prefix}")));
16 }
17 Ok(Self { agent, prefix })
18 }
19
20 #[must_use]
21 pub fn agent(&self) -> &str {
22 &self.agent
23 }
24
25 #[must_use]
27 pub fn session_for(&self, target: &MessageTarget) -> String {
28 let key = conversation_key(target);
29 format!("{}-{:016x}", self.prefix, fnv1a(key.as_bytes()))
30 }
31
32 #[must_use]
34 pub fn request_id_for(&self, message: &InboundMessage) -> String {
35 let mut key = conversation_key(&message.target);
36 key.push('\0');
37 key.push_str(&message.id);
38 format!("im-{}-{:016x}", self.prefix, fnv1a(key.as_bytes()))
39 }
40}
41
42fn conversation_key(target: &MessageTarget) -> String {
43 let mut key = String::with_capacity(target.conversation.as_str().len() + 64);
44 key.push_str(target.channel.as_str());
45 key.push('\0');
46 key.push_str(target.conversation.as_str());
47 if let Some(thread) = target.thread.as_deref() {
48 key.push('\0');
49 key.push_str(thread);
50 }
51 key
52}
53
54fn valid_name(value: &str) -> bool {
55 !value.is_empty()
56 && value.len() <= 64
57 && value
58 .bytes()
59 .all(|byte| byte.is_ascii_alphanumeric() || matches!(byte, b'.' | b'-' | b'_'))
60}
61
62fn fnv1a(bytes: &[u8]) -> u64 {
63 bytes.iter().fold(0xcbf2_9ce4_8422_2325, |hash, byte| {
64 (hash ^ u64::from(*byte)).wrapping_mul(0x0100_0000_01b3)
65 })
66}