Skip to main content

moqtap_client/draft13/
namespace.rs

1/// SUBSCRIBE_ANNOUNCES lifecycle states (draft-12).
2#[derive(Debug, Clone, Copy, PartialEq, Eq)]
3pub enum SubscribeAnnouncesState {
4    /// Initial state before any message is sent.
5    Idle,
6    /// SUBSCRIBE_ANNOUNCES 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/// ANNOUNCE lifecycle states (draft-12).
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub enum AnnounceState {
17    /// Initial state before any message is sent.
18    Idle,
19    /// ANNOUNCE has been sent; awaiting ANNOUNCE_OK or ANNOUNCE_ERROR.
20    Pending,
21    /// Namespace publication is accepted and active.
22    Active,
23    /// Namespace publication has ended (UNANNOUNCE or cancel).
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 the SUBSCRIBE_ANNOUNCES flow (draft-12).
41/// Idle → Pending → Active → Done.
42pub struct SubscribeAnnouncesStateMachine {
43    state: SubscribeAnnouncesState,
44}
45
46impl Default for SubscribeAnnouncesStateMachine {
47    fn default() -> Self {
48        Self::new()
49    }
50}
51
52impl SubscribeAnnouncesStateMachine {
53    /// Creates a new machine in [`SubscribeAnnouncesState::Idle`].
54    pub fn new() -> Self {
55        Self { state: SubscribeAnnouncesState::Idle }
56    }
57
58    /// Returns the current state.
59    pub fn state(&self) -> SubscribeAnnouncesState {
60        self.state
61    }
62
63    /// Idle → Pending.
64    pub fn on_subscribe_announces_sent(&mut self) -> Result<(), NamespaceError> {
65        if self.state == SubscribeAnnouncesState::Idle {
66            self.state = SubscribeAnnouncesState::Pending;
67            Ok(())
68        } else {
69            Err(NamespaceError::InvalidTransition {
70                from: format!("{:?}", self.state),
71                event: "on_subscribe_announces_sent".to_string(),
72            })
73        }
74    }
75
76    /// Pending → Active.
77    pub fn on_subscribe_announces_ok(&mut self) -> Result<(), NamespaceError> {
78        if self.state == SubscribeAnnouncesState::Pending {
79            self.state = SubscribeAnnouncesState::Active;
80            Ok(())
81        } else {
82            Err(NamespaceError::InvalidTransition {
83                from: format!("{:?}", self.state),
84                event: "on_subscribe_announces_ok".to_string(),
85            })
86        }
87    }
88
89    /// Pending → Done.
90    pub fn on_subscribe_announces_error(&mut self) -> Result<(), NamespaceError> {
91        if self.state == SubscribeAnnouncesState::Pending {
92            self.state = SubscribeAnnouncesState::Done;
93            Ok(())
94        } else {
95            Err(NamespaceError::InvalidTransition {
96                from: format!("{:?}", self.state),
97                event: "on_subscribe_announces_error".to_string(),
98            })
99        }
100    }
101
102    /// Active → Done (UNSUBSCRIBE_ANNOUNCES sent).
103    pub fn on_unsubscribe_announces(&mut self) -> Result<(), NamespaceError> {
104        if self.state == SubscribeAnnouncesState::Active {
105            self.state = SubscribeAnnouncesState::Done;
106            Ok(())
107        } else {
108            Err(NamespaceError::InvalidTransition {
109                from: format!("{:?}", self.state),
110                event: "on_unsubscribe_announces".to_string(),
111            })
112        }
113    }
114}
115
116/// State machine for the ANNOUNCE flow (draft-12).
117/// Idle → Pending → Active → Done.
118pub struct AnnounceStateMachine {
119    state: AnnounceState,
120}
121
122impl Default for AnnounceStateMachine {
123    fn default() -> Self {
124        Self::new()
125    }
126}
127
128impl AnnounceStateMachine {
129    /// Creates a new machine in [`AnnounceState::Idle`].
130    pub fn new() -> Self {
131        Self { state: AnnounceState::Idle }
132    }
133
134    /// Returns the current state.
135    pub fn state(&self) -> AnnounceState {
136        self.state
137    }
138
139    /// Idle → Pending (ANNOUNCE sent).
140    pub fn on_announce_sent(&mut self) -> Result<(), NamespaceError> {
141        if self.state == AnnounceState::Idle {
142            self.state = AnnounceState::Pending;
143            Ok(())
144        } else {
145            Err(NamespaceError::InvalidTransition {
146                from: format!("{:?}", self.state),
147                event: "on_announce_sent".to_string(),
148            })
149        }
150    }
151
152    /// Pending → Active (ANNOUNCE_OK received).
153    pub fn on_announce_ok(&mut self) -> Result<(), NamespaceError> {
154        if self.state == AnnounceState::Pending {
155            self.state = AnnounceState::Active;
156            Ok(())
157        } else {
158            Err(NamespaceError::InvalidTransition {
159                from: format!("{:?}", self.state),
160                event: "on_announce_ok".to_string(),
161            })
162        }
163    }
164
165    /// Pending → Done (ANNOUNCE_ERROR received).
166    pub fn on_announce_error(&mut self) -> Result<(), NamespaceError> {
167        if self.state == AnnounceState::Pending {
168            self.state = AnnounceState::Done;
169            Ok(())
170        } else {
171            Err(NamespaceError::InvalidTransition {
172                from: format!("{:?}", self.state),
173                event: "on_announce_error".to_string(),
174            })
175        }
176    }
177
178    /// Active → Done (UNANNOUNCE sent — publisher withdrawing).
179    pub fn on_unannounce(&mut self) -> Result<(), NamespaceError> {
180        if self.state == AnnounceState::Active {
181            self.state = AnnounceState::Done;
182            Ok(())
183        } else {
184            Err(NamespaceError::InvalidTransition {
185                from: format!("{:?}", self.state),
186                event: "on_unannounce".to_string(),
187            })
188        }
189    }
190
191    /// Active → Done (ANNOUNCE_CANCEL received — subscriber cancelling).
192    pub fn on_announce_cancel(&mut self) -> Result<(), NamespaceError> {
193        if self.state == AnnounceState::Active {
194            self.state = AnnounceState::Done;
195            Ok(())
196        } else {
197            Err(NamespaceError::InvalidTransition {
198                from: format!("{:?}", self.state),
199                event: "on_announce_cancel".to_string(),
200            })
201        }
202    }
203}