Skip to main content

moqtap_client/draft16/
track_status.rs

1/// TrackStatus lifecycle states.
2#[derive(Debug, Clone, Copy, PartialEq, Eq)]
3pub enum TrackStatusState {
4    /// Initial state before any TRACK_STATUS message is sent.
5    Idle,
6    /// TRACK_STATUS has been sent; awaiting OK or ERROR.
7    Pending,
8    /// Track status request has completed.
9    Done,
10}
11
12/// Errors that can occur during track status state transitions.
13#[derive(Debug, thiserror::Error, PartialEq, Eq)]
14pub enum TrackStatusError {
15    /// An event was received that is not valid for the current state.
16    #[error("invalid transition from {from:?} on event {event}")]
17    InvalidTransition {
18        /// The state the machine was in when the invalid event arrived.
19        from: TrackStatusState,
20        /// The name of the event that was rejected.
21        event: String,
22    },
23}
24
25/// Pure state machine for a MoQT track status request.
26/// Transitions: Idle -> Pending -> Done.
27pub struct TrackStatusStateMachine {
28    state: TrackStatusState,
29}
30
31impl Default for TrackStatusStateMachine {
32    fn default() -> Self {
33        Self::new()
34    }
35}
36
37impl TrackStatusStateMachine {
38    /// Creates a new state machine in the [`TrackStatusState::Idle`] state.
39    pub fn new() -> Self {
40        Self { state: TrackStatusState::Idle }
41    }
42
43    /// Returns the current state of the track status request.
44    pub fn state(&self) -> TrackStatusState {
45        self.state
46    }
47
48    /// Idle -> Pending (TRACK_STATUS sent).
49    pub fn on_track_status_sent(&mut self) -> Result<(), TrackStatusError> {
50        if self.state == TrackStatusState::Idle {
51            self.state = TrackStatusState::Pending;
52            Ok(())
53        } else {
54            Err(TrackStatusError::InvalidTransition {
55                from: self.state,
56                event: "on_track_status_sent".to_string(),
57            })
58        }
59    }
60
61    /// Pending -> Done (REQUEST_OK received).
62    pub fn on_track_status_ok(&mut self) -> Result<(), TrackStatusError> {
63        if self.state == TrackStatusState::Pending {
64            self.state = TrackStatusState::Done;
65            Ok(())
66        } else {
67            Err(TrackStatusError::InvalidTransition {
68                from: self.state,
69                event: "on_track_status_ok".to_string(),
70            })
71        }
72    }
73
74    /// Pending -> Done (REQUEST_ERROR received).
75    pub fn on_track_status_error(&mut self) -> Result<(), TrackStatusError> {
76        if self.state == TrackStatusState::Pending {
77            self.state = TrackStatusState::Done;
78            Ok(())
79        } else {
80            Err(TrackStatusError::InvalidTransition {
81                from: self.state,
82                event: "on_track_status_error".to_string(),
83            })
84        }
85    }
86}
87
88/// The same transitions, named for the end the request arrives at.
89///
90/// A track status the peer asks for passes through the states in the same
91/// order as one this endpoint asks for, with every message going the other
92/// way: the request arrives instead of leaving and the answer leaves instead
93/// of arriving. Sharing the transitions and not the names is what lets a
94/// refusal say which event was refused rather than the mirror image of it.
95///
96/// Section 9.19 says what the arriving request is: the receiver "treats it
97/// identically as if it had received a SUBSCRIBE message, except it does not
98/// create downstream subscription state or send any Objects". Identical
99/// treatment and no subscription state is why the request gets a machine of
100/// this kind rather than a subscription's, and why what it opens is a record
101/// of its own rather than an entry among the subscriptions the peer holds.
102impl TrackStatusStateMachine {
103    /// Idle -> Pending (TRACK_STATUS received).
104    pub fn on_track_status_received(&mut self) -> Result<(), TrackStatusError> {
105        self.on_track_status_sent().map_err(|_| TrackStatusError::InvalidTransition {
106            from: self.state(),
107            event: "on_track_status_received".to_string(),
108        })
109    }
110
111    /// Pending -> Done (REQUEST_OK sent).
112    pub fn on_track_status_ok_sent(&mut self) -> Result<(), TrackStatusError> {
113        self.on_track_status_ok().map_err(|_| TrackStatusError::InvalidTransition {
114            from: self.state(),
115            event: "on_track_status_ok_sent".to_string(),
116        })
117    }
118
119    /// Pending -> Done (REQUEST_ERROR sent).
120    pub fn on_track_status_error_sent(&mut self) -> Result<(), TrackStatusError> {
121        self.on_track_status_error().map_err(|_| TrackStatusError::InvalidTransition {
122            from: self.state(),
123            event: "on_track_status_error_sent".to_string(),
124        })
125    }
126}