Skip to main content

elara_core/
id.rs

1//! Identity types for ELARA protocol
2//!
3//! All identifiers are 64-bit for wire efficiency while maintaining
4//! sufficient uniqueness for practical swarm sizes.
5
6use std::fmt;
7
8/// Node identity - cryptographic fingerprint (truncated hash of public key)
9#[derive(Clone, Copy, PartialEq, Eq, Hash, Default)]
10pub struct NodeId(pub u64);
11
12impl NodeId {
13    pub const ZERO: NodeId = NodeId(0);
14
15    #[inline]
16    pub fn new(id: u64) -> Self {
17        NodeId(id)
18    }
19
20    #[inline]
21    pub fn to_bytes(self) -> [u8; 8] {
22        self.0.to_le_bytes()
23    }
24
25    #[inline]
26    pub fn from_bytes(bytes: [u8; 8]) -> Self {
27        NodeId(u64::from_le_bytes(bytes))
28    }
29}
30
31impl fmt::Debug for NodeId {
32    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33        write!(f, "Node({:016x})", self.0)
34    }
35}
36
37impl fmt::Display for NodeId {
38    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39        write!(f, "{:016x}", self.0)
40    }
41}
42
43/// Session identity - shared reality space binding
44#[derive(Clone, Copy, PartialEq, Eq, Hash, Default)]
45pub struct SessionId(pub u64);
46
47impl SessionId {
48    pub const ZERO: SessionId = SessionId(0);
49
50    #[inline]
51    pub fn new(id: u64) -> Self {
52        SessionId(id)
53    }
54
55    #[inline]
56    pub fn to_bytes(self) -> [u8; 8] {
57        self.0.to_le_bytes()
58    }
59
60    #[inline]
61    pub fn from_bytes(bytes: [u8; 8]) -> Self {
62        SessionId(u64::from_le_bytes(bytes))
63    }
64}
65
66impl fmt::Debug for SessionId {
67    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68        write!(f, "Session({:016x})", self.0)
69    }
70}
71
72/// State atom identity - unique within a session
73#[derive(Clone, Copy, PartialEq, Eq, Hash, Default)]
74pub struct StateId(pub u64);
75
76impl StateId {
77    pub const ZERO: StateId = StateId(0);
78
79    #[inline]
80    pub fn new(id: u64) -> Self {
81        StateId(id)
82    }
83
84    /// Create a state ID from type prefix and instance ID
85    /// Format: \[type:16\]\[instance:48\]
86    #[inline]
87    pub fn from_type_instance(state_type: u16, instance: u64) -> Self {
88        let id = ((state_type as u64) << 48) | (instance & 0x0000_FFFF_FFFF_FFFF);
89        StateId(id)
90    }
91
92    #[inline]
93    pub fn state_type(self) -> u16 {
94        (self.0 >> 48) as u16
95    }
96
97    #[inline]
98    pub fn instance(self) -> u64 {
99        self.0 & 0x0000_FFFF_FFFF_FFFF
100    }
101
102    #[inline]
103    pub fn to_bytes(self) -> [u8; 8] {
104        self.0.to_le_bytes()
105    }
106
107    #[inline]
108    pub fn from_bytes(bytes: [u8; 8]) -> Self {
109        StateId(u64::from_le_bytes(bytes))
110    }
111}
112
113impl fmt::Debug for StateId {
114    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
115        write!(
116            f,
117            "State({:04x}:{:012x})",
118            self.state_type(),
119            self.instance()
120        )
121    }
122}
123
124impl fmt::Display for StateId {
125    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
126        write!(f, "{:04x}:{:012x}", self.state_type(), self.instance())
127    }
128}
129
130/// Event identity - unique within a session, used for causal ordering
131#[derive(Clone, Copy, PartialEq, Eq, Hash, Default)]
132pub struct EventId {
133    pub node: NodeId,
134    pub seq: u64,
135}
136
137impl EventId {
138    #[inline]
139    pub fn new(node: NodeId, seq: u64) -> Self {
140        EventId { node, seq }
141    }
142}
143
144impl fmt::Debug for EventId {
145    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
146        write!(f, "Event({:016x}:{})", self.node.0, self.seq)
147    }
148}
149
150/// Message identity for text streams
151#[derive(Clone, Copy, PartialEq, Eq, Hash, Default)]
152pub struct MessageId(pub u64);
153
154impl MessageId {
155    #[inline]
156    pub fn new(id: u64) -> Self {
157        MessageId(id)
158    }
159
160    #[inline]
161    pub fn from_event(event_id: &EventId) -> Self {
162        // Combine node and seq into message ID
163        let id = (event_id.node.0 ^ event_id.seq).wrapping_mul(0x517cc1b727220a95);
164        MessageId(id)
165    }
166}
167
168impl fmt::Debug for MessageId {
169    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
170        write!(f, "Msg({:016x})", self.0)
171    }
172}
173
174#[cfg(test)]
175mod tests {
176    use super::*;
177
178    #[test]
179    fn test_node_id_roundtrip() {
180        let id = NodeId::new(0xDEADBEEF_CAFEBABE);
181        let bytes = id.to_bytes();
182        let recovered = NodeId::from_bytes(bytes);
183        assert_eq!(id, recovered);
184    }
185
186    #[test]
187    fn test_state_id_type_instance() {
188        let state_type = 0x0001; // e.g., text stream
189        let instance = 0x0000_1234_5678_9ABC;
190        let id = StateId::from_type_instance(state_type, instance);
191
192        assert_eq!(id.state_type(), state_type);
193        assert_eq!(id.instance(), instance);
194    }
195
196    #[test]
197    fn test_state_id_instance_truncation() {
198        // Instance should be truncated to 48 bits
199        let state_type = 0x0002;
200        let instance = 0xFFFF_FFFF_FFFF_FFFF; // All bits set
201        let id = StateId::from_type_instance(state_type, instance);
202
203        assert_eq!(id.state_type(), state_type);
204        assert_eq!(id.instance(), 0x0000_FFFF_FFFF_FFFF); // Truncated
205    }
206}