moqtap_client/draft15/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}