1use std::collections::{HashMap, HashSet};
2
3use rmux_proto::{SdkWaitId, SdkWaitOwnerId};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
7pub struct SdkWaitKey {
8 owner_id: SdkWaitOwnerId,
9 wait_id: SdkWaitId,
10}
11
12impl SdkWaitKey {
13 #[must_use]
15 pub const fn new(owner_id: SdkWaitOwnerId, wait_id: SdkWaitId) -> Self {
16 Self { owner_id, wait_id }
17 }
18
19 #[must_use]
21 pub const fn owner_id(self) -> SdkWaitOwnerId {
22 self.owner_id
23 }
24
25 #[must_use]
27 pub const fn wait_id(self) -> SdkWaitId {
28 self.wait_id
29 }
30}
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq)]
34pub struct SdkWaitRecord {
35 key: SdkWaitKey,
36 connection_id: u64,
37}
38
39impl SdkWaitRecord {
40 #[must_use]
42 pub const fn key(self) -> SdkWaitKey {
43 self.key
44 }
45
46 #[must_use]
48 pub const fn connection_id(self) -> u64 {
49 self.connection_id
50 }
51}
52
53#[derive(Debug, Clone, Default)]
55pub struct SdkWaitRegistry {
56 next_id_by_owner: HashMap<SdkWaitOwnerId, u64>,
57 records: HashMap<SdkWaitKey, SdkWaitRecord>,
58 by_connection: HashMap<u64, HashSet<SdkWaitKey>>,
59}
60
61impl SdkWaitRegistry {
62 pub fn allocate_id(&mut self, owner_id: SdkWaitOwnerId) -> SdkWaitId {
68 let next = self.next_id_by_owner.entry(owner_id).or_insert(1);
69 let wait_id = SdkWaitId::new(*next);
70 *next = next
71 .checked_add(1)
72 .expect("SDK wait id space exhausted for owner");
73 wait_id
74 }
75
76 pub fn register(
82 &mut self,
83 connection_id: u64,
84 owner_id: SdkWaitOwnerId,
85 wait_id: SdkWaitId,
86 ) -> bool {
87 let key = SdkWaitKey::new(owner_id, wait_id);
88 if self.records.contains_key(&key) {
89 return false;
90 }
91
92 let record = SdkWaitRecord { key, connection_id };
93 self.records.insert(key, record);
94 self.by_connection
95 .entry(connection_id)
96 .or_default()
97 .insert(key);
98 true
99 }
100
101 pub fn remove(
106 &mut self,
107 owner_id: SdkWaitOwnerId,
108 wait_id: SdkWaitId,
109 ) -> Option<SdkWaitRecord> {
110 self.remove_key(SdkWaitKey::new(owner_id, wait_id))
111 }
112
113 pub fn remove_connection(&mut self, connection_id: u64) -> Vec<SdkWaitRecord> {
115 let keys = self
116 .by_connection
117 .remove(&connection_id)
118 .unwrap_or_default()
119 .into_iter()
120 .collect::<Vec<_>>();
121 keys.into_iter()
122 .filter_map(|key| self.remove_record_without_connection_index(key))
123 .collect()
124 }
125
126 #[must_use]
128 pub fn get(&self, owner_id: SdkWaitOwnerId, wait_id: SdkWaitId) -> Option<SdkWaitRecord> {
129 self.records
130 .get(&SdkWaitKey::new(owner_id, wait_id))
131 .copied()
132 }
133
134 #[must_use]
136 pub fn len(&self) -> usize {
137 self.records.len()
138 }
139
140 #[must_use]
142 pub fn is_empty(&self) -> bool {
143 self.records.is_empty()
144 }
145
146 fn remove_key(&mut self, key: SdkWaitKey) -> Option<SdkWaitRecord> {
147 let record = self.records.remove(&key)?;
148 if let Some(keys) = self.by_connection.get_mut(&record.connection_id) {
149 keys.remove(&key);
150 if keys.is_empty() {
151 self.by_connection.remove(&record.connection_id);
152 }
153 }
154 Some(record)
155 }
156
157 fn remove_record_without_connection_index(&mut self, key: SdkWaitKey) -> Option<SdkWaitRecord> {
158 self.records.remove(&key)
159 }
160}
161
162#[cfg(test)]
163mod tests {
164 use super::*;
165
166 fn owner(value: u64) -> SdkWaitOwnerId {
167 SdkWaitOwnerId::new(value)
168 }
169
170 fn wait(value: u64) -> SdkWaitId {
171 SdkWaitId::new(value)
172 }
173
174 #[test]
175 fn wait_ids_are_unique_within_owner_and_scoped_across_owners() {
176 let mut registry = SdkWaitRegistry::default();
177
178 assert_eq!(registry.allocate_id(owner(10)), wait(1));
179 assert_eq!(registry.allocate_id(owner(10)), wait(2));
180 assert_eq!(registry.allocate_id(owner(11)), wait(1));
181 assert_eq!(registry.allocate_id(owner(10)), wait(3));
182 }
183
184 #[test]
185 fn duplicate_late_and_repeated_cancel_are_idempotent() {
186 let mut registry = SdkWaitRegistry::default();
187
188 assert!(registry.register(7, owner(1), wait(1)));
189 assert!(!registry.register(8, owner(1), wait(1)));
190 assert_eq!(registry.len(), 1);
191
192 let removed = registry
193 .remove(owner(1), wait(1))
194 .expect("first cancel removes wait");
195 assert_eq!(removed.connection_id(), 7);
196 assert!(registry.remove(owner(1), wait(1)).is_none());
197 assert!(registry.remove(owner(1), wait(9)).is_none());
198 assert!(registry.is_empty());
199 }
200
201 #[test]
202 fn connection_teardown_removes_only_that_connections_waits() {
203 let mut registry = SdkWaitRegistry::default();
204 assert!(registry.register(1, owner(10), wait(1)));
205 assert!(registry.register(1, owner(10), wait(2)));
206 assert!(registry.register(2, owner(20), wait(1)));
207
208 let removed = registry.remove_connection(1);
209 let removed_keys = removed
210 .iter()
211 .map(|record| record.key())
212 .collect::<HashSet<_>>();
213 assert_eq!(
214 removed_keys,
215 HashSet::from([
216 SdkWaitKey::new(owner(10), wait(1)),
217 SdkWaitKey::new(owner(10), wait(2)),
218 ])
219 );
220 assert_eq!(registry.len(), 1);
221 assert!(registry.get(owner(20), wait(1)).is_some());
222 assert!(registry.remove_connection(1).is_empty());
223 assert_eq!(registry.remove_connection(2).len(), 1);
224 assert!(registry.is_empty());
225 }
226}