Skip to main content

moqtap_client/draft18/
namespace.rs

1/// SUBSCRIBE_NAMESPACE lifecycle states.
2#[derive(Debug, Clone, Copy, PartialEq, Eq)]
3pub enum SubscribeNamespaceState {
4    /// Initial state before any message is sent.
5    Idle,
6    /// SUBSCRIBE_NAMESPACE has been sent; awaiting OK or ERROR.
7    Pending,
8    /// Namespace subscription is accepted and active.
9    Active,
10    /// Namespace subscription has ended.
11    Done,
12}
13
14/// PUBLISH_NAMESPACE lifecycle states.
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub enum PublishNamespaceState {
17    /// Initial state before any message is sent.
18    Idle,
19    /// PUBLISH_NAMESPACE has been sent; awaiting OK or ERROR.
20    Pending,
21    /// Namespace publication is accepted and active.
22    Active,
23    /// Namespace publication has ended.
24    Done,
25}
26
27/// Errors that can occur during namespace state transitions.
28#[derive(Debug, thiserror::Error, PartialEq, Eq)]
29pub enum NamespaceError {
30    /// An event was received that is not valid for the current state.
31    #[error("invalid transition from {from} on event {event}")]
32    InvalidTransition {
33        /// The state the machine was in when the invalid event arrived.
34        from: String,
35        /// The name of the event that was rejected.
36        event: String,
37    },
38}
39
40/// State machine for SUBSCRIBE_NAMESPACE flow.
41/// Idle -> Pending -> Active -> Done.
42pub struct SubscribeNamespaceStateMachine {
43    state: SubscribeNamespaceState,
44}
45
46impl Default for SubscribeNamespaceStateMachine {
47    fn default() -> Self {
48        Self::new()
49    }
50}
51
52impl SubscribeNamespaceStateMachine {
53    /// Creates a new machine in [`SubscribeNamespaceState::Idle`].
54    pub fn new() -> Self {
55        Self { state: SubscribeNamespaceState::Idle }
56    }
57
58    /// Returns the current state of the subscribe-namespace flow.
59    pub fn state(&self) -> SubscribeNamespaceState {
60        self.state
61    }
62
63    /// Idle -> Pending.
64    pub fn on_subscribe_namespace_sent(&mut self) -> Result<(), NamespaceError> {
65        if self.state == SubscribeNamespaceState::Idle {
66            self.state = SubscribeNamespaceState::Pending;
67            Ok(())
68        } else {
69            Err(NamespaceError::InvalidTransition {
70                from: format!("{:?}", self.state),
71                event: "on_subscribe_namespace_sent".to_string(),
72            })
73        }
74    }
75
76    /// Pending -> Active.
77    pub fn on_subscribe_namespace_ok(&mut self) -> Result<(), NamespaceError> {
78        if self.state == SubscribeNamespaceState::Pending {
79            self.state = SubscribeNamespaceState::Active;
80            Ok(())
81        } else {
82            Err(NamespaceError::InvalidTransition {
83                from: format!("{:?}", self.state),
84                event: "on_subscribe_namespace_ok".to_string(),
85            })
86        }
87    }
88
89    /// Pending -> Done.
90    pub fn on_subscribe_namespace_error(&mut self) -> Result<(), NamespaceError> {
91        if self.state == SubscribeNamespaceState::Pending {
92            self.state = SubscribeNamespaceState::Done;
93            Ok(())
94        } else {
95            Err(NamespaceError::InvalidTransition {
96                from: format!("{:?}", self.state),
97                event: "on_subscribe_namespace_error".to_string(),
98            })
99        }
100    }
101
102    /// Pending | Active -> Done, Done -> Done (this request's stream was
103    /// cancelled).
104    ///
105    /// This draft has no UNSUBSCRIBE_NAMESPACE message. Section 3.3.2: "Once a request
106    /// stream has been opened, the request MAY be cancelled by either endpoint."
107    ///
108    /// `Idle` is refused, on the other half of the same sentence: nothing has
109    /// been written, so there is no stream to terminate. `Done` stays `Done` —
110    /// nothing finishes a request stream's send half on the ordinary path, so a
111    /// caller that walks away from a request that has already ended still
112    /// resets the stream, and that reset is an ordinary end rather than a
113    /// fault.
114    pub fn on_request_cancelled(&mut self) -> Result<(), NamespaceError> {
115        match self.state {
116            SubscribeNamespaceState::Pending | SubscribeNamespaceState::Active => {
117                self.state = SubscribeNamespaceState::Done;
118                Ok(())
119            }
120            SubscribeNamespaceState::Done => Ok(()),
121            SubscribeNamespaceState::Idle => Err(NamespaceError::InvalidTransition {
122                from: format!("{:?}", self.state),
123                event: "on_request_cancelled".to_string(),
124            }),
125        }
126    }
127}
128
129/// State machine for PUBLISH_NAMESPACE flow.
130/// Idle -> Pending -> Active -> Done.
131pub struct PublishNamespaceStateMachine {
132    state: PublishNamespaceState,
133}
134
135impl Default for PublishNamespaceStateMachine {
136    fn default() -> Self {
137        Self::new()
138    }
139}
140
141impl PublishNamespaceStateMachine {
142    /// Creates a new machine in [`PublishNamespaceState::Idle`].
143    pub fn new() -> Self {
144        Self { state: PublishNamespaceState::Idle }
145    }
146
147    /// Returns the current state of the publish-namespace flow.
148    pub fn state(&self) -> PublishNamespaceState {
149        self.state
150    }
151
152    /// Idle -> Pending.
153    pub fn on_publish_namespace_sent(&mut self) -> Result<(), NamespaceError> {
154        if self.state == PublishNamespaceState::Idle {
155            self.state = PublishNamespaceState::Pending;
156            Ok(())
157        } else {
158            Err(NamespaceError::InvalidTransition {
159                from: format!("{:?}", self.state),
160                event: "on_publish_namespace_sent".to_string(),
161            })
162        }
163    }
164
165    /// Pending -> Active.
166    pub fn on_publish_namespace_ok(&mut self) -> Result<(), NamespaceError> {
167        if self.state == PublishNamespaceState::Pending {
168            self.state = PublishNamespaceState::Active;
169            Ok(())
170        } else {
171            Err(NamespaceError::InvalidTransition {
172                from: format!("{:?}", self.state),
173                event: "on_publish_namespace_ok".to_string(),
174            })
175        }
176    }
177
178    /// Pending -> Done.
179    pub fn on_publish_namespace_error(&mut self) -> Result<(), NamespaceError> {
180        if self.state == PublishNamespaceState::Pending {
181            self.state = PublishNamespaceState::Done;
182            Ok(())
183        } else {
184            Err(NamespaceError::InvalidTransition {
185                from: format!("{:?}", self.state),
186                event: "on_publish_namespace_error".to_string(),
187            })
188        }
189    }
190
191    /// Pending | Active -> Done, Done -> Done (this request's stream was
192    /// cancelled).
193    ///
194    /// This draft has neither PUBLISH_NAMESPACE_DONE nor
195    /// PUBLISH_NAMESPACE_CANCEL: a publisher withdraws its advertisement, and a
196    /// receiver refuses one, by terminating the stream the PUBLISH_NAMESPACE
197    /// opened. Section 3.3.2: "Once a request
198    /// stream has been opened, the request MAY be cancelled by either endpoint."
199    ///
200    /// `Idle` is refused, on the other half of the same sentence: nothing has
201    /// been written, so there is no stream to terminate. `Done` stays `Done` —
202    /// nothing finishes a request stream's send half on the ordinary path, so a
203    /// caller that walks away from a request that has already ended still
204    /// resets the stream, and that reset is an ordinary end rather than a
205    /// fault.
206    pub fn on_request_cancelled(&mut self) -> Result<(), NamespaceError> {
207        match self.state {
208            PublishNamespaceState::Pending | PublishNamespaceState::Active => {
209                self.state = PublishNamespaceState::Done;
210                Ok(())
211            }
212            PublishNamespaceState::Done => Ok(()),
213            PublishNamespaceState::Idle => Err(NamespaceError::InvalidTransition {
214                from: format!("{:?}", self.state),
215                event: "on_request_cancelled".to_string(),
216            }),
217        }
218    }
219}