1use std::marker::PhantomData;
13use std::time::Instant;
14
15pub trait SessionState: private::Sealed {}
17
18mod private {
19 pub trait Sealed {}
20}
21
22#[derive(Debug, Clone, Copy)]
24pub struct Disconnected;
25
26impl private::Sealed for Disconnected {}
27impl SessionState for Disconnected {}
28
29#[derive(Debug, Clone, Copy)]
31pub struct Connecting;
32
33impl private::Sealed for Connecting {}
34impl SessionState for Connecting {}
35
36#[derive(Debug, Clone)]
38pub struct LogonSent {
39 pub sent_at: Instant,
41}
42
43impl private::Sealed for LogonSent {}
44impl SessionState for LogonSent {}
45
46#[derive(Debug, Clone)]
49pub struct LogonReceived {
50 pub received_at: Instant,
52}
53
54impl private::Sealed for LogonReceived {}
55impl SessionState for LogonReceived {}
56
57#[derive(Debug, Clone, Copy)]
59pub struct Active;
60
61impl private::Sealed for Active {}
62impl SessionState for Active {}
63
64#[derive(Debug, Clone)]
66pub struct Resending {
67 pub begin_seq: u64,
69 pub end_seq: u64,
71}
72
73impl private::Sealed for Resending {}
74impl SessionState for Resending {}
75
76#[derive(Debug, Clone)]
78pub struct LogoutPending {
79 pub sent_at: Instant,
81}
82
83impl private::Sealed for LogoutPending {}
84impl SessionState for LogoutPending {}
85
86#[derive(Debug)]
90pub struct Session<S: SessionState> {
91 pub session_id: String,
93 _state: PhantomData<S>,
95}
96
97impl<S: SessionState> Session<S> {
98 #[must_use]
100 pub fn session_id(&self) -> &str {
101 &self.session_id
102 }
103}
104
105impl Session<Disconnected> {
106 #[must_use]
111 pub fn new(session_id: impl Into<String>) -> Self {
112 Self {
113 session_id: session_id.into(),
114 _state: PhantomData,
115 }
116 }
117
118 #[must_use]
120 pub fn connect(self) -> Session<Connecting> {
121 Session {
122 session_id: self.session_id,
123 _state: PhantomData,
124 }
125 }
126
127 #[must_use]
130 pub fn accept(self) -> Session<Connecting> {
131 Session {
132 session_id: self.session_id,
133 _state: PhantomData,
134 }
135 }
136}
137
138impl Session<Connecting> {
139 #[must_use]
141 pub fn send_logon(self) -> Session<LogonSent> {
142 Session {
143 session_id: self.session_id,
144 _state: PhantomData,
145 }
146 }
147
148 #[must_use]
151 pub fn on_logon_received(self) -> Session<LogonReceived> {
152 Session {
153 session_id: self.session_id,
154 _state: PhantomData,
155 }
156 }
157
158 #[must_use]
160 pub fn disconnect(self) -> Session<Disconnected> {
161 Session {
162 session_id: self.session_id,
163 _state: PhantomData,
164 }
165 }
166}
167
168impl Session<LogonSent> {
169 #[must_use]
171 pub fn on_logon_ack(self) -> Session<Active> {
172 Session {
173 session_id: self.session_id,
174 _state: PhantomData,
175 }
176 }
177
178 #[must_use]
180 pub fn on_logon_reject(self) -> Session<Disconnected> {
181 Session {
182 session_id: self.session_id,
183 _state: PhantomData,
184 }
185 }
186}
187
188impl Session<LogonReceived> {
189 #[must_use]
192 pub fn accept_logon(self) -> Session<Active> {
193 Session {
194 session_id: self.session_id,
195 _state: PhantomData,
196 }
197 }
198
199 #[must_use]
202 pub fn reject_logon(self) -> Session<Disconnected> {
203 Session {
204 session_id: self.session_id,
205 _state: PhantomData,
206 }
207 }
208
209 #[must_use]
212 pub fn on_timeout(self) -> Session<Disconnected> {
213 Session {
214 session_id: self.session_id,
215 _state: PhantomData,
216 }
217 }
218}
219
220impl Session<Active> {
221 #[must_use]
227 pub fn start_resend(self, _begin_seq: u64, _end_seq: u64) -> Session<Resending> {
228 Session {
229 session_id: self.session_id,
230 _state: PhantomData,
231 }
232 }
233
234 #[must_use]
236 pub fn initiate_logout(self) -> Session<LogoutPending> {
237 Session {
238 session_id: self.session_id,
239 _state: PhantomData,
240 }
241 }
242
243 #[must_use]
245 pub fn disconnect(self) -> Session<Disconnected> {
246 Session {
247 session_id: self.session_id,
248 _state: PhantomData,
249 }
250 }
251}
252
253impl Session<Resending> {
254 #[must_use]
256 pub fn resend_complete(self) -> Session<Active> {
257 Session {
258 session_id: self.session_id,
259 _state: PhantomData,
260 }
261 }
262
263 #[must_use]
265 pub fn disconnect(self) -> Session<Disconnected> {
266 Session {
267 session_id: self.session_id,
268 _state: PhantomData,
269 }
270 }
271}
272
273impl Session<LogoutPending> {
274 #[must_use]
276 pub fn on_logout_ack(self) -> Session<Disconnected> {
277 Session {
278 session_id: self.session_id,
279 _state: PhantomData,
280 }
281 }
282
283 #[must_use]
285 pub fn on_timeout(self) -> Session<Disconnected> {
286 Session {
287 session_id: self.session_id,
288 _state: PhantomData,
289 }
290 }
291}
292
293#[cfg(test)]
294mod tests {
295 use super::*;
296
297 #[test]
298 fn test_session_state_transitions() {
299 let session = Session::<Disconnected>::new("TEST");
300 assert_eq!(session.session_id(), "TEST");
301
302 let session = session.connect();
303 let session = session.send_logon();
304 let session = session.on_logon_ack();
305
306 let session = session.initiate_logout();
308 let _session = session.on_logout_ack();
309 }
310
311 #[test]
312 fn test_acceptor_flow() {
313 let session = Session::<Disconnected>::new("ACCEPTOR");
314 let session = session.accept();
315 let session = session.on_logon_received();
316 let session = session.accept_logon();
317
318 let session = session.initiate_logout();
320 let _session = session.on_logout_ack();
321 }
322
323 #[test]
324 fn test_acceptor_reject_flow() {
325 let session = Session::<Disconnected>::new("ACCEPTOR");
326 let session = session.accept();
327 let session = session.on_logon_received();
328 let _session = session.reject_logon();
329 }
330
331 #[test]
332 fn test_acceptor_timeout_flow() {
333 let session = Session::<Disconnected>::new("ACCEPTOR");
334 let session = session.accept();
335 let session = session.on_logon_received();
336 let _session = session.on_timeout();
337 }
338
339 #[test]
340 fn test_resend_flow() {
341 let session = Session::<Disconnected>::new("TEST");
342 let session = session.connect();
343 let session = session.send_logon();
344 let session = session.on_logon_ack();
345
346 let session = session.start_resend(1, 5);
347 let _session = session.resend_complete();
348 }
349}