Skip to main content

moqtap_client/
subscription.rs

1/// Subscription lifecycle states.
2#[derive(Debug, Clone, Copy, PartialEq, Eq)]
3pub enum SubscriptionState {
4    Idle,
5    Subscribing,
6    Active,
7    Done,
8}
9
10#[derive(Debug, thiserror::Error, PartialEq, Eq)]
11pub enum SubscriptionError {
12    #[error("invalid transition from {from:?} on event {event}")]
13    InvalidTransition { from: SubscriptionState, event: String },
14}
15
16/// Pure state machine for a MoQT subscription.
17/// Transitions: Idle → Subscribing → Active → Done.
18pub struct SubscriptionStateMachine {
19    state: SubscriptionState,
20}
21
22impl Default for SubscriptionStateMachine {
23    fn default() -> Self {
24        Self::new()
25    }
26}
27
28impl SubscriptionStateMachine {
29    pub fn new() -> Self {
30        Self { state: SubscriptionState::Idle }
31    }
32
33    pub fn state(&self) -> SubscriptionState {
34        self.state
35    }
36
37    /// Idle → Subscribing (SUBSCRIBE sent).
38    pub fn on_subscribe_sent(&mut self) -> Result<(), SubscriptionError> {
39        if self.state == SubscriptionState::Idle {
40            self.state = SubscriptionState::Subscribing;
41            Ok(())
42        } else {
43            Err(SubscriptionError::InvalidTransition {
44                from: self.state,
45                event: "on_subscribe_sent".to_string(),
46            })
47        }
48    }
49
50    /// Subscribing → Active (SUBSCRIBE_OK received).
51    pub fn on_subscribe_ok(&mut self) -> Result<(), SubscriptionError> {
52        if self.state == SubscriptionState::Subscribing {
53            self.state = SubscriptionState::Active;
54            Ok(())
55        } else {
56            Err(SubscriptionError::InvalidTransition {
57                from: self.state,
58                event: "on_subscribe_ok".to_string(),
59            })
60        }
61    }
62
63    /// Subscribing → Done (SUBSCRIBE_ERROR received).
64    pub fn on_subscribe_error(&mut self) -> Result<(), SubscriptionError> {
65        if self.state == SubscriptionState::Subscribing {
66            self.state = SubscriptionState::Done;
67            Ok(())
68        } else {
69            Err(SubscriptionError::InvalidTransition {
70                from: self.state,
71                event: "on_subscribe_error".to_string(),
72            })
73        }
74    }
75
76    /// Active → Done (UNSUBSCRIBE sent).
77    pub fn on_unsubscribe(&mut self) -> Result<(), SubscriptionError> {
78        if self.state == SubscriptionState::Active {
79            self.state = SubscriptionState::Done;
80            Ok(())
81        } else {
82            Err(SubscriptionError::InvalidTransition {
83                from: self.state,
84                event: "on_unsubscribe".to_string(),
85            })
86        }
87    }
88
89    /// Active → Done (PUBLISH_DONE received).
90    pub fn on_publish_done(&mut self) -> Result<(), SubscriptionError> {
91        if self.state == SubscriptionState::Active {
92            self.state = SubscriptionState::Done;
93            Ok(())
94        } else {
95            Err(SubscriptionError::InvalidTransition {
96                from: self.state,
97                event: "on_publish_done".to_string(),
98            })
99        }
100    }
101}