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, Copy)]
48pub struct Active;
49
50impl private::Sealed for Active {}
51impl SessionState for Active {}
52
53#[derive(Debug, Clone)]
55pub struct Resending {
56 pub begin_seq: u64,
58 pub end_seq: u64,
60}
61
62impl private::Sealed for Resending {}
63impl SessionState for Resending {}
64
65#[derive(Debug, Clone)]
67pub struct LogoutPending {
68 pub sent_at: Instant,
70}
71
72impl private::Sealed for LogoutPending {}
73impl SessionState for LogoutPending {}
74
75#[derive(Debug)]
79pub struct Session<S: SessionState> {
80 pub session_id: String,
82 _state: PhantomData<S>,
84}
85
86impl<S: SessionState> Session<S> {
87 #[must_use]
89 pub fn session_id(&self) -> &str {
90 &self.session_id
91 }
92}
93
94impl Session<Disconnected> {
95 #[must_use]
100 pub fn new(session_id: impl Into<String>) -> Self {
101 Self {
102 session_id: session_id.into(),
103 _state: PhantomData,
104 }
105 }
106
107 #[must_use]
109 pub fn connect(self) -> Session<Connecting> {
110 Session {
111 session_id: self.session_id,
112 _state: PhantomData,
113 }
114 }
115}
116
117impl Session<Connecting> {
118 #[must_use]
120 pub fn send_logon(self) -> Session<LogonSent> {
121 Session {
122 session_id: self.session_id,
123 _state: PhantomData,
124 }
125 }
126
127 #[must_use]
129 pub fn disconnect(self) -> Session<Disconnected> {
130 Session {
131 session_id: self.session_id,
132 _state: PhantomData,
133 }
134 }
135}
136
137impl Session<LogonSent> {
138 #[must_use]
140 pub fn on_logon_ack(self) -> Session<Active> {
141 Session {
142 session_id: self.session_id,
143 _state: PhantomData,
144 }
145 }
146
147 #[must_use]
149 pub fn on_logon_reject(self) -> Session<Disconnected> {
150 Session {
151 session_id: self.session_id,
152 _state: PhantomData,
153 }
154 }
155}
156
157impl Session<Active> {
158 #[must_use]
164 pub fn start_resend(self, _begin_seq: u64, _end_seq: u64) -> Session<Resending> {
165 Session {
166 session_id: self.session_id,
167 _state: PhantomData,
168 }
169 }
170
171 #[must_use]
173 pub fn initiate_logout(self) -> Session<LogoutPending> {
174 Session {
175 session_id: self.session_id,
176 _state: PhantomData,
177 }
178 }
179
180 #[must_use]
182 pub fn disconnect(self) -> Session<Disconnected> {
183 Session {
184 session_id: self.session_id,
185 _state: PhantomData,
186 }
187 }
188}
189
190impl Session<Resending> {
191 #[must_use]
193 pub fn resend_complete(self) -> Session<Active> {
194 Session {
195 session_id: self.session_id,
196 _state: PhantomData,
197 }
198 }
199
200 #[must_use]
202 pub fn disconnect(self) -> Session<Disconnected> {
203 Session {
204 session_id: self.session_id,
205 _state: PhantomData,
206 }
207 }
208}
209
210impl Session<LogoutPending> {
211 #[must_use]
213 pub fn on_logout_ack(self) -> Session<Disconnected> {
214 Session {
215 session_id: self.session_id,
216 _state: PhantomData,
217 }
218 }
219
220 #[must_use]
222 pub fn on_timeout(self) -> Session<Disconnected> {
223 Session {
224 session_id: self.session_id,
225 _state: PhantomData,
226 }
227 }
228}
229
230#[cfg(test)]
231mod tests {
232 use super::*;
233
234 #[test]
235 fn test_session_state_transitions() {
236 let session = Session::<Disconnected>::new("TEST");
237 assert_eq!(session.session_id(), "TEST");
238
239 let session = session.connect();
240 let session = session.send_logon();
241 let session = session.on_logon_ack();
242
243 let session = session.initiate_logout();
245 let _session = session.on_logout_ack();
246 }
247
248 #[test]
249 fn test_resend_flow() {
250 let session = Session::<Disconnected>::new("TEST");
251 let session = session.connect();
252 let session = session.send_logon();
253 let session = session.on_logon_ack();
254
255 let session = session.start_resend(1, 5);
256 let _session = session.resend_complete();
257 }
258}