1#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
31pub enum State {
32 #[default]
34 Init,
35 Negotiating,
37 Ready,
39 Disconnected,
43 Closing,
45 Closed,
47}
48
49impl State {
50 #[must_use]
52 pub(crate) const fn from_u8(v: u8) -> Self {
53 match v {
54 0 => Self::Init,
55 1 => Self::Negotiating,
56 2 => Self::Ready,
57 3 => Self::Disconnected,
58 4 => Self::Closing,
59 _ => Self::Closed,
60 }
61 }
62
63 #[must_use]
65 pub const fn is_ready(&self) -> bool {
66 matches!(self, Self::Ready)
67 }
68
69 #[must_use]
71 pub const fn is_closed(&self) -> bool {
72 matches!(self, Self::Closed)
73 }
74
75 #[must_use]
77 pub const fn is_disconnected(&self) -> bool {
78 matches!(self, Self::Disconnected)
79 }
80
81 #[must_use]
83 #[allow(clippy::match_same_arms)] pub const fn can_transition_to(&self, target: Self) -> bool {
85 use State::{Closed, Closing, Disconnected, Init, Negotiating, Ready};
86
87 match (*self, target) {
88 (Init, Negotiating) => true,
90 (Negotiating, Ready) => true,
91 (Ready, Closing) => true,
92 (Closing, Closed) => true,
93
94 (Ready, Disconnected) => true,
96
97 (Disconnected, Closed) => true,
99
100 (Init | Negotiating | Ready | Closed, Closed) => true,
103
104 _ => false,
106 }
107 }
108}
109
110impl std::fmt::Display for State {
111 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
112 match self {
113 Self::Init => write!(f, "init"),
114 Self::Negotiating => write!(f, "negotiating"),
115 Self::Ready => write!(f, "ready"),
116 Self::Disconnected => write!(f, "disconnected"),
117 Self::Closing => write!(f, "closing"),
118 Self::Closed => write!(f, "closed"),
119 }
120 }
121}
122
123#[cfg(test)]
124mod tests {
125 use super::*;
126
127 #[test]
128 fn valid_forward_transitions() {
129 assert!(State::Init.can_transition_to(State::Negotiating));
130 assert!(State::Negotiating.can_transition_to(State::Ready));
131 assert!(State::Ready.can_transition_to(State::Closing));
132 assert!(State::Closing.can_transition_to(State::Closed));
133 }
134
135 #[test]
136 fn error_transitions_to_closed() {
137 assert!(State::Init.can_transition_to(State::Closed));
138 assert!(State::Negotiating.can_transition_to(State::Closed));
139 assert!(State::Ready.can_transition_to(State::Closed));
140 assert!(State::Closing.can_transition_to(State::Closed));
141 }
142
143 #[test]
144 fn invalid_transitions() {
145 assert!(!State::Ready.can_transition_to(State::Negotiating));
147 assert!(!State::Ready.can_transition_to(State::Init));
148 assert!(!State::Closed.can_transition_to(State::Ready));
149
150 assert!(!State::Init.can_transition_to(State::Ready));
152 assert!(!State::Negotiating.can_transition_to(State::Closing));
153 }
154
155 #[test]
156 fn state_display() {
157 assert_eq!(State::Init.to_string(), "init");
158 assert_eq!(State::Ready.to_string(), "ready");
159 assert_eq!(State::Closed.to_string(), "closed");
160 }
161
162 #[test]
163 fn is_ready() {
164 assert!(!State::Init.is_ready());
165 assert!(!State::Negotiating.is_ready());
166 assert!(State::Ready.is_ready());
167 assert!(!State::Closing.is_ready());
168 assert!(!State::Closed.is_ready());
169 }
170
171 #[test]
172 fn is_closed() {
173 assert!(!State::Init.is_closed());
174 assert!(!State::Ready.is_closed());
175 assert!(State::Closed.is_closed());
176 }
177
178 #[test]
179 fn disconnected_transitions() {
180 assert!(State::Ready.can_transition_to(State::Disconnected));
181 assert!(State::Disconnected.can_transition_to(State::Closed));
182 assert!(!State::Init.can_transition_to(State::Disconnected));
183 assert!(!State::Disconnected.can_transition_to(State::Ready));
184 }
185
186 #[test]
187 fn is_disconnected() {
188 assert!(!State::Ready.is_disconnected());
189 assert!(State::Disconnected.is_disconnected());
190 assert!(!State::Closed.is_disconnected());
191 }
192}