Skip to main content

scosh_core/
lifecycle.rs

1//! Deterministic host lifecycle transitions.
2
3use crate::{errors::SdkError, events::SessionStatus};
4
5#[derive(Clone, Copy, Debug, Eq, PartialEq)]
6pub enum LifecycleEvent {
7    Connected,
8    NetworkLost,
9    Backgrounded,
10    Foregrounded,
11    RemoteExit,
12    Cancelled,
13    Failed,
14    Closed,
15}
16
17#[derive(Clone, Copy, Debug, Eq, PartialEq)]
18pub struct Lifecycle {
19    status: SessionStatus,
20    input_generation: u64,
21    transport_open: bool,
22}
23
24impl Default for Lifecycle {
25    fn default() -> Self {
26        Self::new()
27    }
28}
29
30impl Lifecycle {
31    pub const fn new() -> Self {
32        Self {
33            status: SessionStatus::Connecting,
34            input_generation: 0,
35            transport_open: false,
36        }
37    }
38
39    pub const fn status(self) -> SessionStatus {
40        self.status
41    }
42
43    pub const fn input_generation(self) -> u64 {
44        self.input_generation
45    }
46
47    pub const fn transport_open(self) -> bool {
48        self.transport_open
49    }
50
51    pub fn transition(&mut self, event: LifecycleEvent) -> Result<SessionStatus, SdkError> {
52        let next = match (self.status, event) {
53            (SessionStatus::Connecting, LifecycleEvent::Connected) => {
54                self.transport_open = true;
55                SessionStatus::Live
56            }
57            (SessionStatus::Live, LifecycleEvent::NetworkLost)
58            | (SessionStatus::Suspended, LifecycleEvent::Foregrounded) => {
59                self.transport_open = false;
60                SessionStatus::Recovering
61            }
62            (SessionStatus::Live, LifecycleEvent::Backgrounded) => {
63                self.invalidate_input();
64                self.transport_open = false;
65                SessionStatus::Suspended
66            }
67            (SessionStatus::Recovering, LifecycleEvent::Connected) => {
68                self.transport_open = true;
69                SessionStatus::Live
70            }
71            (_, LifecycleEvent::RemoteExit | LifecycleEvent::Closed) => {
72                self.transport_open = false;
73                SessionStatus::Closed
74            }
75            (_, LifecycleEvent::Cancelled | LifecycleEvent::Failed) => {
76                self.transport_open = false;
77                SessionStatus::Failed
78            }
79            (SessionStatus::Recovering, LifecycleEvent::Backgrounded) => {
80                self.invalidate_input();
81                self.transport_open = false;
82                SessionStatus::Suspended
83            }
84            (status, _) => {
85                return Err(if status == SessionStatus::Closed {
86                    SdkError::SessionClosed
87                } else {
88                    SdkError::TransportUnavailable
89                });
90            }
91        };
92        self.status = next;
93        Ok(next)
94    }
95
96    fn invalidate_input(&mut self) {
97        self.input_generation = self.input_generation.saturating_add(1);
98    }
99}