dstar_gateway_core/session/server/
state.rs

1//! Sealed `ServerState` markers for the server-side typestate.
2//!
3//! Each marker is a zero-sized type that gates which methods are
4//! available on a [`super::ServerSession`]. The seal prevents
5//! downstream crates from adding their own marker types, which would
6//! break the typestate's exhaustiveness proof.
7//!
8//! [`ServerStateKind`] is the runtime discriminator that mirrors the
9//! compile-time markers — used in error variants, diagnostics, and
10//! any path where the `S` phantom has been erased.
11
12/// Sealed marker for server-side session states.
13pub trait ServerState: sealed::Sealed + 'static {}
14
15mod sealed {
16    pub trait Sealed {}
17}
18
19/// New client packet seen, no link request validated yet.
20#[derive(Debug, Clone, Copy)]
21pub struct Unknown;
22
23/// `DPlus`-specific: LINK1 seen and acknowledged, waiting for LINK2.
24///
25/// This state only applies to `DPlus` sessions — `DExtra` and `DCS`
26/// link in a single packet and move directly from [`Unknown`] to
27/// [`Linked`]. The public runtime discriminator
28/// [`ServerStateKind`] collapses `Link1Received` into
29/// [`ServerStateKind::Unknown`] because external consumers typically
30/// only care about "not linked yet" vs "linked", not the details
31/// of the `DPlus` two-step handshake.
32#[derive(Debug, Clone, Copy)]
33pub struct Link1Received;
34
35/// LINK request received and authorized, client is linked.
36#[derive(Debug, Clone, Copy)]
37pub struct Linked;
38
39/// Voice stream in progress on this client.
40#[derive(Debug, Clone, Copy)]
41pub struct Streaming;
42
43/// UNLINK request seen, client being torn down.
44#[derive(Debug, Clone, Copy)]
45pub struct Unlinking;
46
47/// Terminal — client disconnected.
48#[derive(Debug, Clone, Copy)]
49pub struct Closed;
50
51impl sealed::Sealed for Unknown {}
52impl ServerState for Unknown {}
53impl sealed::Sealed for Link1Received {}
54impl ServerState for Link1Received {}
55impl sealed::Sealed for Linked {}
56impl ServerState for Linked {}
57impl sealed::Sealed for Streaming {}
58impl ServerState for Streaming {}
59impl sealed::Sealed for Unlinking {}
60impl ServerState for Unlinking {}
61impl sealed::Sealed for Closed {}
62impl ServerState for Closed {}
63
64/// Runtime discriminator mirroring the typestate markers.
65#[non_exhaustive]
66#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
67pub enum ServerStateKind {
68    /// Unknown.
69    Unknown,
70    /// Linked.
71    Linked,
72    /// Streaming.
73    Streaming,
74    /// Unlinking.
75    Unlinking,
76    /// Closed.
77    Closed,
78}
79
80#[cfg(test)]
81mod tests {
82    use super::{Closed, Linked, ServerStateKind, Streaming, Unknown, Unlinking};
83
84    #[test]
85    fn states_are_zero_sized() {
86        assert_eq!(size_of::<Unknown>(), 0);
87        assert_eq!(size_of::<Linked>(), 0);
88        assert_eq!(size_of::<Streaming>(), 0);
89        assert_eq!(size_of::<Unlinking>(), 0);
90        assert_eq!(size_of::<Closed>(), 0);
91    }
92
93    #[test]
94    fn state_kind_variants_distinct() {
95        assert_ne!(ServerStateKind::Unknown, ServerStateKind::Linked);
96        assert_ne!(ServerStateKind::Linked, ServerStateKind::Streaming);
97        assert_ne!(ServerStateKind::Streaming, ServerStateKind::Unlinking);
98        assert_ne!(ServerStateKind::Unlinking, ServerStateKind::Closed);
99    }
100}