1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
use std::fmt;
use tokio::net::tcp::split::TcpStreamWriteHalf;

use crate::types::Address;

// Internal state representation. Identical to `ClientState` but tracks internal implementation
// details such as `TcpStreamWriteHalf`.
//
// I would rather use an `Encoder` (instead of the raw `TcpStreamWriteHalf`) that operates
// on a `ClientControl` enum. Unfortunately, when I attempted this, it was painful to make the
// payload passed to publish be of type `&[u8]` instead of `Vec<u8>` without a clone. So for
// now, the writer operates at the tcp layer writing raw bytes while the reader uses a custom
// codec.
pub enum ConnectionState {
    Connected(Address, TcpStreamWriteHalf),
    Connecting(Address),
    Disconnected,
    // If we are coming from a connected state, we have a `TcpStreamWriteHalf` to close
    Disconnecting(Option<TcpStreamWriteHalf>),
}

#[derive(Debug)]
pub enum StateTransition {
    ToConnecting(Address),
    ToConnected(TcpStreamWriteHalf),
    ToDisconnecting,
    ToDisconnected,
}

// Used to return data from a state transition
pub enum StateTransitionResult {
    None,
    Writer(TcpStreamWriteHalf),
}

/// Client states
///
/// ```text
///                                      State Diagram
///                            +--------+-----------------------------------------+
///                            |        |                                         |
///                            |        v                                         v
/// +----------------+     +---+--------+---+     +----------------+     +--------+-------+
/// |                |     |                |     |                |     |                |
/// |  Disconnected  +---->+   Connecting   +---->+   Connected    +---->+ Disconnecting  |
/// |                |     |                |     |                |     |                |
/// +-------+--------+     +----------------+     +--------+-------+     +--------+-------+
///         ^                                              |                      |
///         |                                              |                      |
///         +----------------------------------------------+----------------------+
/// ```
#[derive(Clone, Debug)]
pub enum ClientState {
    /// The client is connected to an address.
    Connected(Address),
    /// The client is connecting to an address.
    Connecting(Address),
    /// The client is disconnected.
    Disconnected,
    /// The client has been instructed to disconnect by calling
    /// [`disconnect`](struct.Client.html#method.disconnect).
    Disconnecting,
}

impl ClientState {
    /// Is the state `Connected`?
    pub fn is_connected(&self) -> bool {
        if let Self::Connected(_) = self {
            return true;
        }
        false
    }

    /// Is the state `Connecting`?
    pub fn is_connecting(&self) -> bool {
        if let Self::Connecting(_) = self {
            return true;
        }
        false
    }

    /// Is the state `Disconnected`?
    pub fn is_disconnected(&self) -> bool {
        if let Self::Disconnected = self {
            return true;
        }
        false
    }

    /// Is the state `Disconnecting`?
    pub fn is_disconnecting(&self) -> bool {
        if let Self::Disconnecting = self {
            return true;
        }
        false
    }
}

impl From<&ConnectionState> for ClientState {
    fn from(s: &ConnectionState) -> Self {
        match s {
            ConnectionState::Connected(address, _) => ClientState::Connected(address.clone()),
            ConnectionState::Connecting(address) => ClientState::Connecting(address.clone()),
            ConnectionState::Disconnected => ClientState::Disconnected,
            ConnectionState::Disconnecting(_) => ClientState::Disconnecting,
        }
    }
}

impl fmt::Display for ClientState {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            ClientState::Connected(address) => write!(f, "Connected({})", address)?,
            ClientState::Connecting(address) => write!(f, "Connecting({})", address)?,
            ClientState::Disconnected => write!(f, "Disconnected")?,
            ClientState::Disconnecting => write!(f, "Disconnecting")?,
        }
        Ok(())
    }
}