alpine/session/
state.rs

1use std::time::{Duration, Instant};
2
3#[derive(Debug, Clone, PartialEq, Eq)]
4pub enum SessionState {
5    Init,
6    Handshake,
7    Authenticated { since: Instant },
8    Ready { since: Instant },
9    Streaming { since: Instant },
10    Failed(String),
11    Closed,
12}
13
14impl SessionState {
15    pub fn can_transition(&self, next: &SessionState) -> bool {
16        use SessionState::*;
17        match (self, next) {
18            (Init, Handshake) => true,
19            (Handshake, Authenticated { .. }) => true,
20            (Authenticated { .. }, Ready { .. }) => true,
21            (Ready { .. }, Streaming { .. }) => true,
22            // terminal moves
23            (_, Failed(_)) => true,
24            (_, Closed) => true,
25            _ => false,
26        }
27    }
28
29    pub fn transition(self, next: SessionState) -> Result<SessionState, SessionStateError> {
30        if self.can_transition(&next) {
31            Ok(next)
32        } else {
33            Err(SessionStateError::InvalidTransition(format!(
34                "cannot transition from {:?} to {:?}",
35                self, next
36            )))
37        }
38    }
39
40    pub fn is_failed(&self) -> bool {
41        matches!(self, SessionState::Failed(_))
42    }
43
44    pub fn is_closed(&self) -> bool {
45        matches!(self, SessionState::Closed)
46    }
47
48    pub fn check_timeout(&self, timeout: Duration, now: Instant) -> bool {
49        match self {
50            SessionState::Authenticated { since }
51            | SessionState::Ready { since }
52            | SessionState::Streaming { since } => now.duration_since(*since) > timeout,
53            _ => false,
54        }
55    }
56}
57
58#[derive(Debug, thiserror::Error)]
59pub enum SessionStateError {
60    #[error("invalid state transition: {0}")]
61    InvalidTransition(String),
62}