capnweb_core/protocol/
ids.rs

1use serde::{Deserialize, Serialize};
2use std::fmt;
3
4/// Import ID - represents an entry in the import table
5/// Positive IDs (1, 2, 3...) are chosen by the importing side
6/// Negative IDs (-1, -2, -3...) are chosen by the exporting side
7/// ID 0 is reserved for the "main" interface
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
9#[serde(transparent)]
10pub struct ImportId(pub i64);
11
12impl ImportId {
13    /// Create a new import ID for the main interface
14    pub fn main() -> Self {
15        ImportId(0)
16    }
17
18    /// Check if this is the main interface ID
19    pub fn is_main(&self) -> bool {
20        self.0 == 0
21    }
22
23    /// Check if this ID was allocated locally (positive)
24    pub fn is_local(&self) -> bool {
25        self.0 > 0
26    }
27
28    /// Check if this ID was allocated remotely (negative)
29    pub fn is_remote(&self) -> bool {
30        self.0 < 0
31    }
32
33    /// Convert to the corresponding export ID on the other side
34    pub fn to_export_id(&self) -> ExportId {
35        ExportId(-self.0)
36    }
37}
38
39impl fmt::Display for ImportId {
40    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41        write!(f, "Import#{}", self.0)
42    }
43}
44
45/// Export ID - represents an entry in the export table
46/// Negative IDs (-1, -2, -3...) are chosen by the exporting side
47/// Positive IDs (1, 2, 3...) are chosen by the importing side
48/// ID 0 is reserved for the "main" interface
49#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
50#[serde(transparent)]
51pub struct ExportId(pub i64);
52
53impl ExportId {
54    /// Create a new export ID for the main interface
55    pub fn main() -> Self {
56        ExportId(0)
57    }
58
59    /// Check if this is the main interface ID
60    pub fn is_main(&self) -> bool {
61        self.0 == 0
62    }
63
64    /// Check if this ID was allocated locally (negative)
65    pub fn is_local(&self) -> bool {
66        self.0 < 0
67    }
68
69    /// Check if this ID was allocated remotely (positive)
70    pub fn is_remote(&self) -> bool {
71        self.0 > 0
72    }
73
74    /// Convert to the corresponding import ID on the other side
75    pub fn to_import_id(&self) -> ImportId {
76        ImportId(-self.0)
77    }
78}
79
80impl fmt::Display for ExportId {
81    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
82        write!(f, "Export#{}", self.0)
83    }
84}
85
86/// ID allocator for managing import and export IDs
87#[derive(Debug)]
88pub struct IdAllocator {
89    next_positive: std::sync::atomic::AtomicI64,
90    next_negative: std::sync::atomic::AtomicI64,
91}
92
93impl IdAllocator {
94    /// Create a new ID allocator
95    pub fn new() -> Self {
96        Self {
97            next_positive: std::sync::atomic::AtomicI64::new(1),
98            next_negative: std::sync::atomic::AtomicI64::new(-1),
99        }
100    }
101
102    /// Allocate a new local import ID (positive)
103    pub fn allocate_import(&self) -> ImportId {
104        let id = self
105            .next_positive
106            .fetch_add(1, std::sync::atomic::Ordering::SeqCst);
107        ImportId(id)
108    }
109
110    /// Allocate a new local export ID (negative)
111    pub fn allocate_export(&self) -> ExportId {
112        let id = self
113            .next_negative
114            .fetch_sub(1, std::sync::atomic::Ordering::SeqCst);
115        ExportId(id)
116    }
117
118    /// Register a remote import ID (negative)
119    pub fn register_remote_import(&self, id: i64) -> ImportId {
120        // Remote imports are negative from our perspective
121        ImportId(id)
122    }
123
124    /// Register a remote export ID (positive)
125    pub fn register_remote_export(&self, id: i64) -> ExportId {
126        // Remote exports are positive from our perspective
127        ExportId(id)
128    }
129}
130
131impl Default for IdAllocator {
132    fn default() -> Self {
133        Self::new()
134    }
135}
136
137#[cfg(test)]
138mod tests {
139    use super::*;
140
141    #[test]
142    fn test_main_ids() {
143        let import = ImportId::main();
144        let export = ExportId::main();
145
146        assert!(import.is_main());
147        assert!(export.is_main());
148        assert_eq!(import.0, 0);
149        assert_eq!(export.0, 0);
150    }
151
152    #[test]
153    fn test_local_remote_detection() {
154        let local_import = ImportId(5);
155        let remote_import = ImportId(-3);
156        let local_export = ExportId(-2);
157        let remote_export = ExportId(4);
158
159        assert!(local_import.is_local());
160        assert!(!local_import.is_remote());
161
162        assert!(!remote_import.is_local());
163        assert!(remote_import.is_remote());
164
165        assert!(local_export.is_local());
166        assert!(!local_export.is_remote());
167
168        assert!(!remote_export.is_local());
169        assert!(remote_export.is_remote());
170    }
171
172    #[test]
173    fn test_id_conversion() {
174        let import = ImportId(5);
175        let export = import.to_export_id();
176        assert_eq!(export, ExportId(-5));
177
178        let import2 = export.to_import_id();
179        assert_eq!(import2, ImportId(5));
180    }
181
182    #[test]
183    fn test_id_allocator() {
184        let allocator = IdAllocator::new();
185
186        // Test import allocation (positive)
187        let import1 = allocator.allocate_import();
188        let import2 = allocator.allocate_import();
189        assert_eq!(import1, ImportId(1));
190        assert_eq!(import2, ImportId(2));
191
192        // Test export allocation (negative)
193        let export1 = allocator.allocate_export();
194        let export2 = allocator.allocate_export();
195        assert_eq!(export1, ExportId(-1));
196        assert_eq!(export2, ExportId(-2));
197
198        // Test remote registration
199        let remote_import = allocator.register_remote_import(-5);
200        let remote_export = allocator.register_remote_export(7);
201        assert_eq!(remote_import, ImportId(-5));
202        assert_eq!(remote_export, ExportId(7));
203    }
204
205    #[test]
206    fn test_display() {
207        let import = ImportId(42);
208        let export = ExportId(-17);
209
210        assert_eq!(format!("{}", import), "Import#42");
211        assert_eq!(format!("{}", export), "Export#-17");
212    }
213}