rants/types/
state.rs

1use std::fmt;
2use tokio::io::WriteHalf;
3
4use crate::{tls_or_tcp_stream::TlsOrTcpStream, types::Address};
5
6// Internal state representation. Identical to `ClientState` but tracks internal implementation
7// details such as `WriteHalf`.
8//
9// I would rather use an `Encoder` (instead of the raw `WriteHalf`) that operates
10// on a `ClientControl` enum. Unfortunately, when I attempted this, it was painful to make the
11// payload passed to publish be of type `&[u8]` instead of `Vec<u8>` without a clone. So for
12// now, the writer operates at the tcp layer writing raw bytes while the reader uses a custom
13// codec.
14pub enum ConnectionState {
15    Connected(Address, WriteHalf<TlsOrTcpStream>),
16    Connecting(Address),
17    Disconnected,
18    // If we are coming from a connected state, we have a `WriteHalf` to close
19    Disconnecting(Option<WriteHalf<TlsOrTcpStream>>),
20}
21
22#[derive(Debug)]
23#[allow(clippy::enum_variant_names)]
24pub enum StateTransition {
25    ToConnecting(Address),
26    ToConnected(WriteHalf<TlsOrTcpStream>),
27    ToDisconnecting,
28    ToDisconnected,
29}
30
31// Used to return data from a state transition
32pub enum StateTransitionResult {
33    None,
34    Writer(WriteHalf<TlsOrTcpStream>),
35}
36
37/// Client states
38///
39/// ```text
40///                                      State Diagram
41///                            +--------+-----------------------------------------+
42///                            |        |                                         |
43///                            |        v                                         v
44/// +----------------+     +---+--------+---+     +----------------+     +--------+-------+
45/// |                |     |                |     |                |     |                |
46/// |  Disconnected  +---->+   Connecting   +---->+   Connected    +---->+ Disconnecting  |
47/// |                |     |                |     |                |     |                |
48/// +-------+--------+     +----------------+     +--------+-------+     +--------+-------+
49///         ^                                              |                      |
50///         |                                              |                      |
51///         +----------------------------------------------+----------------------+
52/// ```
53#[derive(Clone, Debug)]
54pub enum ClientState {
55    /// The client is connected to an address.
56    Connected(Address),
57    /// The client is connecting to an address.
58    Connecting(Address),
59    /// The client is disconnected.
60    Disconnected,
61    /// The client has been instructed to disconnect by calling
62    /// [`disconnect`](struct.Client.html#method.disconnect).
63    Disconnecting,
64}
65
66impl ClientState {
67    /// Is the state `Connected`?
68    pub fn is_connected(&self) -> bool {
69        if let Self::Connected(_) = self {
70            return true;
71        }
72        false
73    }
74
75    /// Is the state `Connecting`?
76    pub fn is_connecting(&self) -> bool {
77        if let Self::Connecting(_) = self {
78            return true;
79        }
80        false
81    }
82
83    /// Is the state `Disconnected`?
84    pub fn is_disconnected(&self) -> bool {
85        if let Self::Disconnected = self {
86            return true;
87        }
88        false
89    }
90
91    /// Is the state `Disconnecting`?
92    pub fn is_disconnecting(&self) -> bool {
93        if let Self::Disconnecting = self {
94            return true;
95        }
96        false
97    }
98}
99
100impl From<&ConnectionState> for ClientState {
101    fn from(s: &ConnectionState) -> Self {
102        match s {
103            ConnectionState::Connected(address, _) => ClientState::Connected(address.clone()),
104            ConnectionState::Connecting(address) => ClientState::Connecting(address.clone()),
105            ConnectionState::Disconnected => ClientState::Disconnected,
106            ConnectionState::Disconnecting(_) => ClientState::Disconnecting,
107        }
108    }
109}
110
111impl fmt::Display for ClientState {
112    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
113        match self {
114            ClientState::Connected(address) => write!(f, "Connected({})", address)?,
115            ClientState::Connecting(address) => write!(f, "Connecting({})", address)?,
116            ClientState::Disconnected => write!(f, "Disconnected")?,
117            ClientState::Disconnecting => write!(f, "Disconnecting")?,
118        }
119        Ok(())
120    }
121}