1use std::time::Instant;
25
26pub trait SessionState: private::Sealed {}
28
29mod private {
30 pub trait Sealed {}
31}
32
33#[derive(Debug, Clone, Copy)]
35pub struct Disconnected;
36
37impl private::Sealed for Disconnected {}
38impl SessionState for Disconnected {}
39
40#[derive(Debug, Clone, Copy)]
42pub struct Connecting;
43
44impl private::Sealed for Connecting {}
45impl SessionState for Connecting {}
46
47#[derive(Debug, Clone, Copy)]
49pub struct LogonSent {
50 pub sent_at: Instant,
52}
53
54impl private::Sealed for LogonSent {}
55impl SessionState for LogonSent {}
56
57#[derive(Debug, Clone, Copy)]
60pub struct LogonReceived {
61 pub received_at: Instant,
63}
64
65impl private::Sealed for LogonReceived {}
66impl SessionState for LogonReceived {}
67
68#[derive(Debug, Clone, Copy)]
70pub struct Active;
71
72impl private::Sealed for Active {}
73impl SessionState for Active {}
74
75#[derive(Debug, Clone, Copy)]
77pub struct Resending {
78 pub begin_seq: u64,
80 pub end_seq: u64,
85}
86
87impl private::Sealed for Resending {}
88impl SessionState for Resending {}
89
90#[derive(Debug, Clone, Copy)]
92pub struct LogoutPending {
93 pub sent_at: Instant,
95}
96
97impl private::Sealed for LogoutPending {}
98impl SessionState for LogoutPending {}
99
100#[derive(Debug)]
105pub struct Session<S: SessionState> {
106 pub session_id: String,
108 state: S,
110}
111
112impl<S: SessionState> Session<S> {
113 #[must_use]
115 pub fn session_id(&self) -> &str {
116 &self.session_id
117 }
118
119 #[must_use]
121 pub const fn state(&self) -> &S {
122 &self.state
123 }
124
125 fn transition<N: SessionState>(self, next: N) -> Session<N> {
130 Session {
131 session_id: self.session_id,
132 state: next,
133 }
134 }
135}
136
137impl Session<Disconnected> {
138 #[must_use]
143 pub fn new(session_id: impl Into<String>) -> Self {
144 Self {
145 session_id: session_id.into(),
146 state: Disconnected,
147 }
148 }
149
150 #[must_use]
152 pub fn connect(self) -> Session<Connecting> {
153 self.transition(Connecting)
154 }
155
156 #[must_use]
159 pub fn accept(self) -> Session<Connecting> {
160 self.transition(Connecting)
161 }
162}
163
164impl Session<Connecting> {
165 #[must_use]
168 pub fn send_logon(self) -> Session<LogonSent> {
169 self.transition(LogonSent {
170 sent_at: Instant::now(),
171 })
172 }
173
174 #[must_use]
177 pub fn on_logon_received(self) -> Session<LogonReceived> {
178 self.transition(LogonReceived {
179 received_at: Instant::now(),
180 })
181 }
182
183 #[must_use]
185 pub fn disconnect(self) -> Session<Disconnected> {
186 self.transition(Disconnected)
187 }
188}
189
190impl Session<LogonSent> {
191 #[must_use]
193 pub const fn sent_at(&self) -> Instant {
194 self.state.sent_at
195 }
196
197 #[must_use]
199 pub fn on_logon_ack(self) -> Session<Active> {
200 self.transition(Active)
201 }
202
203 #[must_use]
205 pub fn on_logon_reject(self) -> Session<Disconnected> {
206 self.transition(Disconnected)
207 }
208}
209
210impl Session<LogonReceived> {
211 #[must_use]
214 pub const fn received_at(&self) -> Instant {
215 self.state.received_at
216 }
217
218 #[must_use]
221 pub fn accept_logon(self) -> Session<Active> {
222 self.transition(Active)
223 }
224
225 #[must_use]
228 pub fn reject_logon(self) -> Session<Disconnected> {
229 self.transition(Disconnected)
230 }
231
232 #[must_use]
235 pub fn on_timeout(self) -> Session<Disconnected> {
236 self.transition(Disconnected)
237 }
238}
239
240impl Session<Active> {
241 #[must_use]
249 pub fn start_resend(self, begin_seq: u64, end_seq: u64) -> Session<Resending> {
250 self.transition(Resending { begin_seq, end_seq })
251 }
252
253 #[must_use]
256 pub fn initiate_logout(self) -> Session<LogoutPending> {
257 self.transition(LogoutPending {
258 sent_at: Instant::now(),
259 })
260 }
261
262 #[must_use]
264 pub fn disconnect(self) -> Session<Disconnected> {
265 self.transition(Disconnected)
266 }
267}
268
269impl Session<Resending> {
270 #[must_use]
272 pub const fn begin_seq(&self) -> u64 {
273 self.state.begin_seq
274 }
275
276 #[must_use]
279 pub const fn end_seq(&self) -> u64 {
280 self.state.end_seq
281 }
282
283 #[must_use]
285 pub fn resend_complete(self) -> Session<Active> {
286 self.transition(Active)
287 }
288
289 #[must_use]
291 pub fn disconnect(self) -> Session<Disconnected> {
292 self.transition(Disconnected)
293 }
294}
295
296impl Session<LogoutPending> {
297 #[must_use]
299 pub const fn sent_at(&self) -> Instant {
300 self.state.sent_at
301 }
302
303 #[must_use]
305 pub fn on_logout_ack(self) -> Session<Disconnected> {
306 self.transition(Disconnected)
307 }
308
309 #[must_use]
311 pub fn on_timeout(self) -> Session<Disconnected> {
312 self.transition(Disconnected)
313 }
314}
315
316#[cfg(test)]
317mod tests {
318 use super::*;
319
320 #[test]
321 fn test_session_state_transitions() {
322 let session = Session::<Disconnected>::new("TEST");
323 assert_eq!(session.session_id(), "TEST");
324
325 let session = session.connect();
326 let session = session.send_logon();
327 let session = session.on_logon_ack();
328
329 let session = session.initiate_logout();
331 let _session = session.on_logout_ack();
332 }
333
334 #[test]
335 fn test_acceptor_flow() {
336 let session = Session::<Disconnected>::new("ACCEPTOR");
337 let session = session.accept();
338 let session = session.on_logon_received();
339 let session = session.accept_logon();
340
341 let session = session.initiate_logout();
343 let _session = session.on_logout_ack();
344 }
345
346 #[test]
347 fn test_acceptor_reject_flow() {
348 let session = Session::<Disconnected>::new("ACCEPTOR");
349 let session = session.accept();
350 let session = session.on_logon_received();
351 let _session = session.reject_logon();
352 }
353
354 #[test]
355 fn test_acceptor_timeout_flow() {
356 let session = Session::<Disconnected>::new("ACCEPTOR");
357 let session = session.accept();
358 let session = session.on_logon_received();
359 let _session = session.on_timeout();
360 }
361
362 #[test]
363 fn test_resend_flow() {
364 let session = Session::<Disconnected>::new("TEST");
365 let session = session.connect();
366 let session = session.send_logon();
367 let session = session.on_logon_ack();
368
369 let session = session.start_resend(1, 5);
370 let _session = session.resend_complete();
371 }
372
373 #[test]
376 fn test_send_logon_records_the_send_instant() {
377 let before = Instant::now();
378 let session = Session::<Disconnected>::new("TEST").connect().send_logon();
379 let after = Instant::now();
380
381 assert!(session.sent_at() >= before);
382 assert!(session.sent_at() <= after);
383 assert_eq!(session.state().sent_at, session.sent_at());
384 }
385
386 #[test]
387 fn test_on_logon_received_records_the_arrival_instant() {
388 let before = Instant::now();
389 let session = Session::<Disconnected>::new("ACCEPTOR")
390 .accept()
391 .on_logon_received();
392 let after = Instant::now();
393
394 assert!(session.received_at() >= before);
395 assert!(session.received_at() <= after);
396 }
397
398 #[test]
399 fn test_initiate_logout_records_the_send_instant() {
400 let before = Instant::now();
401 let session = Session::<Disconnected>::new("TEST")
402 .connect()
403 .send_logon()
404 .on_logon_ack()
405 .initiate_logout();
406 let after = Instant::now();
407
408 assert!(session.sent_at() >= before);
409 assert!(session.sent_at() <= after);
410 }
411
412 #[test]
413 fn test_start_resend_keeps_the_requested_range() {
414 let session = Session::<Disconnected>::new("TEST")
415 .connect()
416 .send_logon()
417 .on_logon_ack()
418 .start_resend(7, 16);
419
420 assert_eq!(session.begin_seq(), 7);
421 assert_eq!(session.end_seq(), 16);
422 assert_eq!(session.state().begin_seq, 7);
423 }
424
425 #[test]
426 fn test_start_resend_keeps_the_open_ended_range() {
427 let session = Session::<Disconnected>::new("TEST")
430 .connect()
431 .send_logon()
432 .on_logon_ack()
433 .start_resend(42, 0);
434
435 assert_eq!(session.begin_seq(), 42);
436 assert_eq!(session.end_seq(), 0);
437 }
438
439 #[test]
440 fn test_session_id_survives_every_transition() {
441 let session = Session::<Disconnected>::new("PERSISTENT")
442 .connect()
443 .send_logon()
444 .on_logon_ack();
445 assert_eq!(session.session_id(), "PERSISTENT");
446
447 let session = session.start_resend(1, 2).resend_complete();
448 assert_eq!(session.session_id(), "PERSISTENT");
449
450 let session = session.initiate_logout();
451 assert_eq!(session.session_id(), "PERSISTENT");
452
453 let session = session.on_logout_ack();
454 assert_eq!(session.session_id(), "PERSISTENT");
455 }
456
457 #[test]
458 fn test_stateless_states_stay_zero_sized() {
459 use std::mem::size_of;
460
461 assert_eq!(size_of::<Disconnected>(), 0);
462 assert_eq!(size_of::<Connecting>(), 0);
463 assert_eq!(size_of::<Active>(), 0);
464 assert_eq!(size_of::<Session<Active>>(), size_of::<String>());
465 }
466}