liminal_sdk/connection/
lifecycle.rs1use alloc::boxed::Box;
2use alloc::format;
3use alloc::string::String;
4use alloc::vec::Vec;
5use core::fmt;
6use core::pin::Pin;
7use core::task::{Context, Poll};
8
9use futures_core::Stream;
10
11use crate::SdkError;
12
13#[derive(Clone, Debug, PartialEq, Eq)]
15pub enum ConnectionState {
16 Connecting,
18 Connected,
20 Reconnecting,
22 Disconnected {
24 reason: DisconnectReason,
26 },
27}
28
29#[derive(Clone, Copy, Debug, PartialEq, Eq)]
31pub enum DisconnectReason {
32 Normal,
34 Error,
36 Timeout,
38}
39
40#[derive(Clone, Debug, PartialEq, Eq)]
42pub struct ConnectionEvent {
43 pub previous: ConnectionState,
45 pub current: ConnectionState,
47}
48
49impl ConnectionEvent {
50 #[must_use]
52 pub const fn new(previous: ConnectionState, current: ConnectionState) -> Self {
53 Self { previous, current }
54 }
55}
56
57pub struct ConnectionEvents<S> {
62 inner: S,
63}
64
65impl<S> ConnectionEvents<S> {
66 #[must_use]
68 pub const fn new(inner: S) -> Self {
69 Self { inner }
70 }
71
72 #[must_use]
74 pub const fn inner(&self) -> &S {
75 &self.inner
76 }
77
78 #[must_use]
80 pub fn into_inner(self) -> S {
81 self.inner
82 }
83}
84
85impl<S: Clone> Clone for ConnectionEvents<S> {
86 fn clone(&self) -> Self {
87 Self {
88 inner: self.inner.clone(),
89 }
90 }
91}
92
93impl<S> fmt::Debug for ConnectionEvents<S> {
94 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
95 formatter.debug_struct("ConnectionEvents").finish()
96 }
97}
98
99impl<S> Stream for ConnectionEvents<S>
100where
101 S: Stream<Item = ConnectionEvent> + Unpin,
102{
103 type Item = ConnectionEvent;
104
105 fn poll_next(mut self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<Option<Self::Item>> {
106 let stream = &mut self.as_mut().get_mut().inner;
107 Pin::new(stream).poll_next(context)
108 }
109}
110
111type ConnectionObserver = Box<dyn FnMut(&ConnectionEvent) + Send>;
112
113pub struct ConnectionLifecycle {
115 state: ConnectionState,
116 observers: Vec<ConnectionObserver>,
117}
118
119impl ConnectionLifecycle {
120 #[must_use]
122 pub const fn new() -> Self {
123 Self {
124 state: ConnectionState::Connecting,
125 observers: Vec::new(),
126 }
127 }
128
129 #[must_use]
131 pub const fn state(&self) -> &ConnectionState {
132 &self.state
133 }
134
135 pub fn observe(&mut self, observer: impl FnMut(&ConnectionEvent) + Send + 'static) {
137 self.observers.push(Box::new(observer));
138 }
139
140 pub fn connect(&mut self) -> Result<(), SdkError> {
146 match self.state {
147 ConnectionState::Disconnected { .. } => {
148 self.transition(ConnectionState::Connecting);
149 Ok(())
150 }
151 _ => Err(invalid_transition(&self.state, "Connecting")),
152 }
153 }
154
155 pub fn connected(&mut self) -> Result<(), SdkError> {
161 match self.state {
162 ConnectionState::Connecting | ConnectionState::Reconnecting => {
163 self.transition(ConnectionState::Connected);
164 Ok(())
165 }
166 _ => Err(invalid_transition(&self.state, "Connected")),
167 }
168 }
169
170 pub fn reconnect_started(&mut self) -> Result<(), SdkError> {
180 match self.state {
181 ConnectionState::Connecting | ConnectionState::Connected => {
182 self.transition(ConnectionState::Reconnecting);
183 Ok(())
184 }
185 ConnectionState::Reconnecting => Err(invalid_transition(&self.state, "Reconnecting")),
186 ConnectionState::Disconnected { .. } => {
187 Err(invalid_transition(&self.state, "Reconnecting"))
188 }
189 }
190 }
191
192 pub fn disconnect(&mut self, reason: DisconnectReason) -> Result<(), SdkError> {
198 match self.state {
199 ConnectionState::Connecting
200 | ConnectionState::Connected
201 | ConnectionState::Reconnecting => {
202 self.transition(ConnectionState::Disconnected { reason });
203 Ok(())
204 }
205 ConnectionState::Disconnected { .. } => {
206 Err(invalid_transition(&self.state, "Disconnected"))
207 }
208 }
209 }
210
211 fn transition(&mut self, next: ConnectionState) {
212 let previous = core::mem::replace(&mut self.state, next);
213 let event = ConnectionEvent::new(previous, self.state.clone());
214
215 for observer in &mut self.observers {
216 observer(&event);
217 }
218 }
219}
220
221impl Default for ConnectionLifecycle {
222 fn default() -> Self {
223 Self::new()
224 }
225}
226
227impl fmt::Debug for ConnectionLifecycle {
228 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
229 formatter
230 .debug_struct("ConnectionLifecycle")
231 .field("state", &self.state)
232 .field("observers", &self.observers.len())
233 .finish()
234 }
235}
236
237fn invalid_transition(previous: &ConnectionState, requested: &str) -> SdkError {
238 connection_error(format!(
239 "invalid connection transition from {previous:?} to {requested}"
240 ))
241}
242
243const fn connection_error(description: String) -> SdkError {
244 SdkError::Connection { description }
245}
246
247#[cfg(test)]
248mod tests;