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}