Skip to main content

moqtap_client/draft07/
fetch.rs

1/// Fetch lifecycle states.
2#[derive(Debug, Clone, Copy, PartialEq, Eq)]
3pub enum FetchState {
4    /// Initial state before any FETCH message is sent.
5    Idle,
6    /// FETCH has been sent; awaiting OK or ERROR.
7    Pending,
8    /// FETCH_OK received; data is being received on the stream.
9    Receiving,
10    /// Fetch has ended (error, cancel, FIN, or reset).
11    Done,
12}
13
14/// Errors that can occur during fetch state transitions.
15#[derive(Debug, thiserror::Error, PartialEq, Eq)]
16pub enum FetchError {
17    /// An event was received that is not valid for the current state.
18    #[error("invalid transition from {from:?} on event {event}")]
19    InvalidTransition {
20        /// The state the machine was in when the invalid event arrived.
21        from: FetchState,
22        /// The name of the event that was rejected.
23        event: String,
24    },
25}
26
27/// Pure state machine for a MoQT fetch request (draft-07).
28/// Transitions: Idle → Pending → Receiving → Done.
29pub struct FetchStateMachine {
30    state: FetchState,
31}
32
33impl Default for FetchStateMachine {
34    fn default() -> Self {
35        Self::new()
36    }
37}
38
39impl FetchStateMachine {
40    /// Creates a new state machine in the [`FetchState::Idle`] state.
41    pub fn new() -> Self {
42        Self { state: FetchState::Idle }
43    }
44
45    /// Returns the current state of the fetch request.
46    pub fn state(&self) -> FetchState {
47        self.state
48    }
49
50    /// Idle → Pending (FETCH sent).
51    pub fn on_fetch_sent(&mut self) -> Result<(), FetchError> {
52        if self.state == FetchState::Idle {
53            self.state = FetchState::Pending;
54            Ok(())
55        } else {
56            Err(FetchError::InvalidTransition {
57                from: self.state,
58                event: "on_fetch_sent".to_string(),
59            })
60        }
61    }
62
63    /// Pending → Receiving (FETCH_OK received).
64    pub fn on_fetch_ok(&mut self) -> Result<(), FetchError> {
65        if self.state == FetchState::Pending {
66            self.state = FetchState::Receiving;
67            Ok(())
68        } else {
69            Err(FetchError::InvalidTransition {
70                from: self.state,
71                event: "on_fetch_ok".to_string(),
72            })
73        }
74    }
75
76    /// Pending → Done (FETCH_ERROR received).
77    pub fn on_fetch_error(&mut self) -> Result<(), FetchError> {
78        if self.state == FetchState::Pending {
79            self.state = FetchState::Done;
80            Ok(())
81        } else {
82            Err(FetchError::InvalidTransition {
83                from: self.state,
84                event: "on_fetch_error".to_string(),
85            })
86        }
87    }
88
89    /// Pending|Receiving → Done (FETCH_CANCEL sent).
90    pub fn on_fetch_cancel(&mut self) -> Result<(), FetchError> {
91        if self.state == FetchState::Pending || self.state == FetchState::Receiving {
92            self.state = FetchState::Done;
93            Ok(())
94        } else {
95            Err(FetchError::InvalidTransition {
96                from: self.state,
97                event: "on_fetch_cancel".to_string(),
98            })
99        }
100    }
101
102    /// Receiving → Done (stream FIN received).
103    pub fn on_stream_fin(&mut self) -> Result<(), FetchError> {
104        if self.state == FetchState::Receiving {
105            self.state = FetchState::Done;
106            Ok(())
107        } else {
108            Err(FetchError::InvalidTransition {
109                from: self.state,
110                event: "on_stream_fin".to_string(),
111            })
112        }
113    }
114
115    /// Receiving → Done (stream RESET received).
116    pub fn on_stream_reset(&mut self) -> Result<(), FetchError> {
117        if self.state == FetchState::Receiving {
118            self.state = FetchState::Done;
119            Ok(())
120        } else {
121            Err(FetchError::InvalidTransition {
122                from: self.state,
123                event: "on_stream_reset".to_string(),
124            })
125        }
126    }
127}