use std::marker::PhantomData;
use std::time::Instant;
pub trait SessionState: private::Sealed {}
mod private {
pub trait Sealed {}
}
#[derive(Debug, Clone, Copy)]
pub struct Disconnected;
impl private::Sealed for Disconnected {}
impl SessionState for Disconnected {}
#[derive(Debug, Clone, Copy)]
pub struct Connecting;
impl private::Sealed for Connecting {}
impl SessionState for Connecting {}
#[derive(Debug, Clone)]
pub struct LogonSent {
pub sent_at: Instant,
}
impl private::Sealed for LogonSent {}
impl SessionState for LogonSent {}
#[derive(Debug, Clone)]
pub struct LogonReceived {
pub received_at: Instant,
}
impl private::Sealed for LogonReceived {}
impl SessionState for LogonReceived {}
#[derive(Debug, Clone, Copy)]
pub struct Active;
impl private::Sealed for Active {}
impl SessionState for Active {}
#[derive(Debug, Clone)]
pub struct Resending {
pub begin_seq: u64,
pub end_seq: u64,
}
impl private::Sealed for Resending {}
impl SessionState for Resending {}
#[derive(Debug, Clone)]
pub struct LogoutPending {
pub sent_at: Instant,
}
impl private::Sealed for LogoutPending {}
impl SessionState for LogoutPending {}
#[derive(Debug)]
pub struct Session<S: SessionState> {
pub session_id: String,
_state: PhantomData<S>,
}
impl<S: SessionState> Session<S> {
#[must_use]
pub fn session_id(&self) -> &str {
&self.session_id
}
}
impl Session<Disconnected> {
#[must_use]
pub fn new(session_id: impl Into<String>) -> Self {
Self {
session_id: session_id.into(),
_state: PhantomData,
}
}
#[must_use]
pub fn connect(self) -> Session<Connecting> {
Session {
session_id: self.session_id,
_state: PhantomData,
}
}
#[must_use]
pub fn accept(self) -> Session<Connecting> {
Session {
session_id: self.session_id,
_state: PhantomData,
}
}
}
impl Session<Connecting> {
#[must_use]
pub fn send_logon(self) -> Session<LogonSent> {
Session {
session_id: self.session_id,
_state: PhantomData,
}
}
#[must_use]
pub fn on_logon_received(self) -> Session<LogonReceived> {
Session {
session_id: self.session_id,
_state: PhantomData,
}
}
#[must_use]
pub fn disconnect(self) -> Session<Disconnected> {
Session {
session_id: self.session_id,
_state: PhantomData,
}
}
}
impl Session<LogonSent> {
#[must_use]
pub fn on_logon_ack(self) -> Session<Active> {
Session {
session_id: self.session_id,
_state: PhantomData,
}
}
#[must_use]
pub fn on_logon_reject(self) -> Session<Disconnected> {
Session {
session_id: self.session_id,
_state: PhantomData,
}
}
}
impl Session<LogonReceived> {
#[must_use]
pub fn accept_logon(self) -> Session<Active> {
Session {
session_id: self.session_id,
_state: PhantomData,
}
}
#[must_use]
pub fn reject_logon(self) -> Session<Disconnected> {
Session {
session_id: self.session_id,
_state: PhantomData,
}
}
#[must_use]
pub fn on_timeout(self) -> Session<Disconnected> {
Session {
session_id: self.session_id,
_state: PhantomData,
}
}
}
impl Session<Active> {
#[must_use]
pub fn start_resend(self, _begin_seq: u64, _end_seq: u64) -> Session<Resending> {
Session {
session_id: self.session_id,
_state: PhantomData,
}
}
#[must_use]
pub fn initiate_logout(self) -> Session<LogoutPending> {
Session {
session_id: self.session_id,
_state: PhantomData,
}
}
#[must_use]
pub fn disconnect(self) -> Session<Disconnected> {
Session {
session_id: self.session_id,
_state: PhantomData,
}
}
}
impl Session<Resending> {
#[must_use]
pub fn resend_complete(self) -> Session<Active> {
Session {
session_id: self.session_id,
_state: PhantomData,
}
}
#[must_use]
pub fn disconnect(self) -> Session<Disconnected> {
Session {
session_id: self.session_id,
_state: PhantomData,
}
}
}
impl Session<LogoutPending> {
#[must_use]
pub fn on_logout_ack(self) -> Session<Disconnected> {
Session {
session_id: self.session_id,
_state: PhantomData,
}
}
#[must_use]
pub fn on_timeout(self) -> Session<Disconnected> {
Session {
session_id: self.session_id,
_state: PhantomData,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_session_state_transitions() {
let session = Session::<Disconnected>::new("TEST");
assert_eq!(session.session_id(), "TEST");
let session = session.connect();
let session = session.send_logon();
let session = session.on_logon_ack();
let session = session.initiate_logout();
let _session = session.on_logout_ack();
}
#[test]
fn test_acceptor_flow() {
let session = Session::<Disconnected>::new("ACCEPTOR");
let session = session.accept();
let session = session.on_logon_received();
let session = session.accept_logon();
let session = session.initiate_logout();
let _session = session.on_logout_ack();
}
#[test]
fn test_acceptor_reject_flow() {
let session = Session::<Disconnected>::new("ACCEPTOR");
let session = session.accept();
let session = session.on_logon_received();
let _session = session.reject_logon();
}
#[test]
fn test_acceptor_timeout_flow() {
let session = Session::<Disconnected>::new("ACCEPTOR");
let session = session.accept();
let session = session.on_logon_received();
let _session = session.on_timeout();
}
#[test]
fn test_resend_flow() {
let session = Session::<Disconnected>::new("TEST");
let session = session.connect();
let session = session.send_logon();
let session = session.on_logon_ack();
let session = session.start_resend(1, 5);
let _session = session.resend_complete();
}
}