Skip to main content

helix_im/state/
values.rs

1//! IM 状态机使用的值对象;只承载确定性数据,不含 I/O。
2
3/// mattermost 26 字符 `[a-z0-9]` ID;Copy newtype,热路径零分配 key(HX-C005)。
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
5pub struct Id26([u8; 26]);
6
7impl Id26 {
8    /// 边界零信任构造:仅恰 26 字节且全 `[a-z0-9]` 时 Some。
9    pub fn from_str(s: &str) -> Option<Self> {
10        let b = s.as_bytes();
11        if b.len() != 26
12            || !b
13                .iter()
14                .all(|c| c.is_ascii_lowercase() || c.is_ascii_digit())
15        {
16            return None;
17        }
18        let mut arr = [0u8; 26];
19        arr.copy_from_slice(b);
20        Some(Self(arr))
21    }
22
23    /// 零拷贝取字符串视图。
24    pub fn as_str(&self) -> &str {
25        // SAFETY: Id26 只能经 from_str 构造,内部恒为合法 UTF-8。
26        unsafe { std::str::from_utf8_unchecked(&self.0) }
27    }
28
29    pub fn as_bytes(&self) -> &[u8; 26] {
30        &self.0
31    }
32
33    /// 派生确定性 u64(FNV-1a,非加密)。
34    pub fn hash_u64(&self) -> u64 {
35        let mut h: u64 = 0xcbf2_9ce4_8422_2325;
36        for &b in &self.0 {
37            h ^= b as u64;
38            h = h.wrapping_mul(0x0000_0100_0000_01b3);
39        }
40        h
41    }
42}
43
44/// 频道 ID(mattermost 26 字符 ID,不透明 Copy)。
45#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
46pub struct ChannelId(pub Id26);
47
48impl ChannelId {
49    pub fn from_str(s: &str) -> Option<Self> {
50        Id26::from_str(s).map(Self)
51    }
52
53    pub fn as_str(&self) -> &str {
54        self.0.as_str()
55    }
56
57    pub fn as_bytes(&self) -> &[u8; 26] {
58        self.0.as_bytes()
59    }
60
61    pub fn hash_u64(&self) -> u64 {
62        self.0.hash_u64()
63    }
64}
65
66/// Server 消息 ID(echo 到时的真实 ID)。
67#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
68pub struct ServerId(pub Id26);
69
70impl ServerId {
71    pub fn from_str(s: &str) -> Option<Self> {
72        Id26::from_str(s).map(Self)
73    }
74
75    pub fn as_str(&self) -> &str {
76        self.0.as_str()
77    }
78}
79
80/// 判断服务端 post 主键;兼容 Java cses 的 24 位 ID 与 Go/Mattermost 的 26 位 ID。
81pub fn is_canonical_post_id(s: &str) -> bool {
82    matches!(s.len(), 24 | 26)
83        && s.as_bytes()
84            .iter()
85            .all(|c| c.is_ascii_lowercase() || c.is_ascii_digit())
86}
87
88/// Helix 铸造并持有的临时消息 ID(乐观落库 PK / HTTP / echo 对账锚)。
89#[derive(Debug, Clone, PartialEq, Eq, Hash)]
90pub struct TemporaryId(pub String);
91
92impl TemporaryId {
93    /// 用 step 时钟与 module 内单调序号铸造确定性 ID。
94    ///
95    /// 相同 tick/clock 序列产生相同结果;单个 ImModule 生命周期内 O(1)
96    /// 分配且不重复。公开 command payload 不参与 ID 选择。
97    pub(crate) fn mint(now_ms: u64, sequence: u64) -> Self {
98        Self(format!("helix_tmp_{now_ms:016x}_{sequence:016x}"))
99    }
100}
101
102/// per-channel 事件序号(单调递增)。
103#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
104pub struct Seq(pub u64);
105
106impl Seq {
107    pub fn next(self) -> Self {
108        Self(self.0 + 1)
109    }
110}
111
112#[derive(Debug, Clone, Copy, PartialEq, Eq)]
113pub enum SendStatus {
114    Local,
115    Sending,
116    Sent,
117    UnSend,
118}
119
120/// 同一 channel 同时只允许一个在途 sync Http。
121#[derive(Debug, Clone, Copy, PartialEq, Eq)]
122pub struct InflightSync(pub helix_core::Correlation);
123
124/// per-channel 游标(单调推进,MAX guard)。
125#[derive(Debug, Clone, Copy)]
126pub struct Cursor {
127    pub value: Seq,
128}
129
130impl Cursor {
131    pub fn new(value: Seq) -> Self {
132        Self { value }
133    }
134
135    pub fn try_advance(&mut self, next: Seq) -> bool {
136        if next > self.value {
137            self.value = next;
138            true
139        } else {
140            false
141        }
142    }
143
144    pub fn value(&self) -> Seq {
145        self.value
146    }
147}
148
149#[derive(Debug, Clone, Copy, PartialEq, Eq)]
150pub enum ConnState {
151    Disconnected,
152    Connecting,
153    Connected,
154    Closing,
155}