Skip to main content

moqtap_codec/
dispatch.rs

1//! Unified types and version-aware decode/encode for runtime draft dispatch.
2//!
3//! This module provides wrapper enums (`Any*`) that hold any enabled draft's
4//! types and dispatch encoding/decoding based on [`DraftVersion`].
5//!
6//! Each enum variant is gated on its draft feature flag. Enable multiple draft
7//! features (e.g. `draft07` + `draft14`) for runtime dispatch between drafts.
8
9use bytes::{Buf, BufMut};
10
11use crate::error::CodecError;
12use crate::version::DraftVersion;
13
14/// Generates a dispatch enum with one variant per enabled draft feature.
15///
16/// Each variant wraps the draft-specific type and delegates encode/decode
17/// to the appropriate draft module.
18macro_rules! dispatch_enum {
19    (
20        $(#[$meta:meta])*
21        $vis:vis enum $name:ident {
22            $(
23                #[cfg(feature = $feat:literal)]
24                $variant:ident => $module:path,
25            )+
26        }
27        decode($decode_fn:ident);
28        encode($encode_fn:ident -> $encode_ret:ty);
29    ) => {
30        $(#[$meta])*
31        $vis enum $name {
32            $(
33                #[cfg(feature = $feat)]
34                #[doc = concat!("Draft-", $feat, " variant.")]
35                $variant($module),
36            )+
37        }
38
39        impl $name {
40            /// Decode from wire using the specified draft version.
41            #[allow(unused_variables)]
42            pub fn decode(
43                version: DraftVersion,
44                buf: &mut impl Buf,
45            ) -> Result<Self, CodecError> {
46                match version {
47                    $(
48                        #[cfg(feature = $feat)]
49                        DraftVersion::$variant => {
50                            <$module>::$decode_fn(buf).map($name::$variant)
51                        }
52                    )+
53                    #[allow(unreachable_patterns)]
54                    _ => Err(CodecError::UnsupportedDraft(
55                        format!("draft {:?} not enabled via feature flag", version),
56                    )),
57                }
58            }
59
60            /// Encode to wire using the appropriate draft's format.
61            #[allow(unused_variables, unreachable_code)]
62            pub fn encode(&self, buf: &mut impl BufMut) -> $encode_ret {
63                match self {
64                    $(
65                        #[cfg(feature = $feat)]
66                        $name::$variant(inner) => inner.$encode_fn(buf),
67                    )+
68                    #[allow(unreachable_patterns)]
69                    _ => unreachable!("AnyXxx enum has no enabled variants"),
70                }
71            }
72
73            /// Returns the draft version this value belongs to.
74            #[allow(unreachable_code)]
75            pub fn draft(&self) -> DraftVersion {
76                match self {
77                    $(
78                        #[cfg(feature = $feat)]
79                        $name::$variant(_) => DraftVersion::$variant,
80                    )+
81                    #[allow(unreachable_patterns)]
82                    _ => unreachable!("AnyXxx enum has no enabled variants"),
83                }
84            }
85        }
86    };
87}
88
89// ── Control messages ────────────────────────────────────────
90
91dispatch_enum! {
92    /// A control message from any enabled draft.
93    #[derive(Debug, Clone)]
94    pub enum AnyControlMessage {
95        #[cfg(feature = "draft07")]
96        Draft07 => crate::draft07::message::ControlMessage,
97        #[cfg(feature = "draft08")]
98        Draft08 => crate::draft08::message::ControlMessage,
99        #[cfg(feature = "draft09")]
100        Draft09 => crate::draft09::message::ControlMessage,
101        #[cfg(feature = "draft10")]
102        Draft10 => crate::draft10::message::ControlMessage,
103        #[cfg(feature = "draft11")]
104        Draft11 => crate::draft11::message::ControlMessage,
105        #[cfg(feature = "draft12")]
106        Draft12 => crate::draft12::message::ControlMessage,
107        #[cfg(feature = "draft13")]
108        Draft13 => crate::draft13::message::ControlMessage,
109        #[cfg(feature = "draft14")]
110        Draft14 => crate::draft14::message::ControlMessage,
111        #[cfg(feature = "draft15")]
112        Draft15 => crate::draft15::message::ControlMessage,
113        #[cfg(feature = "draft16")]
114        Draft16 => crate::draft16::message::ControlMessage,
115        #[cfg(feature = "draft17")]
116        Draft17 => crate::draft17::message::ControlMessage,
117    }
118    decode(decode);
119    encode(encode -> Result<(), CodecError>);
120}
121
122impl AnyControlMessage {
123    /// Returns `true` if this is a CLIENT_SETUP or SERVER_SETUP message.
124    pub fn is_setup(&self) -> bool {
125        match self {
126            #[cfg(feature = "draft07")]
127            AnyControlMessage::Draft07(m) => matches!(
128                m,
129                crate::draft07::message::ControlMessage::ClientSetup(_)
130                    | crate::draft07::message::ControlMessage::ServerSetup(_)
131            ),
132            #[cfg(feature = "draft08")]
133            AnyControlMessage::Draft08(m) => matches!(
134                m,
135                crate::draft08::message::ControlMessage::ClientSetup(_)
136                    | crate::draft08::message::ControlMessage::ServerSetup(_)
137            ),
138            #[cfg(feature = "draft09")]
139            AnyControlMessage::Draft09(m) => matches!(
140                m,
141                crate::draft09::message::ControlMessage::ClientSetup(_)
142                    | crate::draft09::message::ControlMessage::ServerSetup(_)
143            ),
144            #[cfg(feature = "draft10")]
145            AnyControlMessage::Draft10(m) => matches!(
146                m,
147                crate::draft10::message::ControlMessage::ClientSetup(_)
148                    | crate::draft10::message::ControlMessage::ServerSetup(_)
149            ),
150            #[cfg(feature = "draft11")]
151            AnyControlMessage::Draft11(m) => matches!(
152                m,
153                crate::draft11::message::ControlMessage::ClientSetup(_)
154                    | crate::draft11::message::ControlMessage::ServerSetup(_)
155            ),
156            #[cfg(feature = "draft12")]
157            AnyControlMessage::Draft12(m) => matches!(
158                m,
159                crate::draft12::message::ControlMessage::ClientSetup(_)
160                    | crate::draft12::message::ControlMessage::ServerSetup(_)
161            ),
162            #[cfg(feature = "draft13")]
163            AnyControlMessage::Draft13(m) => matches!(
164                m,
165                crate::draft13::message::ControlMessage::ClientSetup(_)
166                    | crate::draft13::message::ControlMessage::ServerSetup(_)
167            ),
168            #[cfg(feature = "draft14")]
169            AnyControlMessage::Draft14(m) => matches!(
170                m,
171                crate::draft14::message::ControlMessage::ClientSetup(_)
172                    | crate::draft14::message::ControlMessage::ServerSetup(_)
173            ),
174            #[cfg(feature = "draft15")]
175            AnyControlMessage::Draft15(m) => matches!(
176                m,
177                crate::draft15::message::ControlMessage::ClientSetup(_)
178                    | crate::draft15::message::ControlMessage::ServerSetup(_)
179            ),
180            #[cfg(feature = "draft16")]
181            AnyControlMessage::Draft16(m) => matches!(
182                m,
183                crate::draft16::message::ControlMessage::ClientSetup(_)
184                    | crate::draft16::message::ControlMessage::ServerSetup(_)
185            ),
186            #[cfg(feature = "draft17")]
187            AnyControlMessage::Draft17(m) => {
188                matches!(m, crate::draft17::message::ControlMessage::Setup(_))
189            }
190            #[allow(unreachable_patterns)]
191            _ => false,
192        }
193    }
194}
195
196// ── Data stream headers ─────────────────────────────────────
197
198dispatch_enum! {
199    /// A subgroup header from any enabled draft.
200    #[derive(Debug, Clone)]
201    pub enum AnySubgroupHeader {
202        #[cfg(feature = "draft07")]
203        Draft07 => crate::draft07::data_stream::SubgroupHeader,
204        #[cfg(feature = "draft08")]
205        Draft08 => crate::draft08::data_stream::SubgroupHeader,
206        #[cfg(feature = "draft09")]
207        Draft09 => crate::draft09::data_stream::SubgroupHeader,
208        #[cfg(feature = "draft10")]
209        Draft10 => crate::draft10::data_stream::SubgroupHeader,
210        #[cfg(feature = "draft11")]
211        Draft11 => crate::draft11::data_stream::SubgroupHeader,
212        #[cfg(feature = "draft12")]
213        Draft12 => crate::draft12::data_stream::SubgroupHeader,
214        #[cfg(feature = "draft13")]
215        Draft13 => crate::draft13::data_stream::SubgroupHeader,
216        #[cfg(feature = "draft14")]
217        Draft14 => crate::draft14::data_stream::SubgroupHeader,
218        #[cfg(feature = "draft15")]
219        Draft15 => crate::draft15::data_stream::SubgroupHeader,
220        #[cfg(feature = "draft16")]
221        Draft16 => crate::draft16::data_stream::SubgroupHeader,
222        #[cfg(feature = "draft17")]
223        Draft17 => crate::draft17::data_stream::SubgroupHeader,
224    }
225    decode(decode);
226    encode(encode -> ());
227}
228
229dispatch_enum! {
230    /// An object header from any enabled draft.
231    #[derive(Debug, Clone)]
232    pub enum AnyObjectHeader {
233        #[cfg(feature = "draft07")]
234        Draft07 => crate::draft07::data_stream::ObjectHeader,
235        #[cfg(feature = "draft08")]
236        Draft08 => crate::draft08::data_stream::ObjectHeader,
237        #[cfg(feature = "draft09")]
238        Draft09 => crate::draft09::data_stream::ObjectHeader,
239        #[cfg(feature = "draft10")]
240        Draft10 => crate::draft10::data_stream::ObjectHeader,
241        #[cfg(feature = "draft11")]
242        Draft11 => crate::draft11::data_stream::ObjectHeader,
243        #[cfg(feature = "draft12")]
244        Draft12 => crate::draft12::data_stream::ObjectHeader,
245        #[cfg(feature = "draft13")]
246        Draft13 => crate::draft13::data_stream::ObjectHeader,
247        // NOTE: drafts 14-17 have no standalone ObjectHeader. Subgroup
248        // objects on those drafts use delta-encoded object IDs and
249        // header-typed extension/properties presence flags, so
250        // decoding requires stateful per-stream context. Callers must
251        // use each draft's `SubgroupObjectReader` (or
252        // `FetchObject::decode` for fetch streams) directly.
253    }
254    decode(decode);
255    encode(encode -> ());
256}
257
258dispatch_enum! {
259    /// A datagram header from any enabled draft.
260    #[derive(Debug, Clone)]
261    pub enum AnyDatagramHeader {
262        #[cfg(feature = "draft07")]
263        Draft07 => crate::draft07::data_stream::DatagramHeader,
264        #[cfg(feature = "draft08")]
265        Draft08 => crate::draft08::data_stream::DatagramHeader,
266        #[cfg(feature = "draft09")]
267        Draft09 => crate::draft09::data_stream::DatagramHeader,
268        #[cfg(feature = "draft10")]
269        Draft10 => crate::draft10::data_stream::DatagramHeader,
270        #[cfg(feature = "draft11")]
271        Draft11 => crate::draft11::data_stream::DatagramHeader,
272        #[cfg(feature = "draft12")]
273        Draft12 => crate::draft12::data_stream::DatagramHeader,
274        #[cfg(feature = "draft13")]
275        Draft13 => crate::draft13::data_stream::DatagramHeader,
276        #[cfg(feature = "draft14")]
277        Draft14 => crate::draft14::data_stream::DatagramObject,
278        #[cfg(feature = "draft15")]
279        Draft15 => crate::draft15::data_stream::DatagramHeader,
280        #[cfg(feature = "draft16")]
281        Draft16 => crate::draft16::data_stream::DatagramHeader,
282        #[cfg(feature = "draft17")]
283        Draft17 => crate::draft17::data_stream::DatagramHeader,
284    }
285    decode(decode);
286    encode(encode -> ());
287}
288
289dispatch_enum! {
290    /// A fetch header from any enabled draft.
291    ///
292    /// Note: Header structure varies significantly across drafts.
293    /// Draft-07 has a minimal fetch header, Draft-14 has a full header.
294    #[derive(Debug, Clone)]
295    pub enum AnyFetchHeader {
296        #[cfg(feature = "draft07")]
297        Draft07 => crate::draft07::data_stream::FetchHeader,
298        #[cfg(feature = "draft08")]
299        Draft08 => crate::draft08::data_stream::FetchHeader,
300        #[cfg(feature = "draft09")]
301        Draft09 => crate::draft09::data_stream::FetchHeader,
302        #[cfg(feature = "draft10")]
303        Draft10 => crate::draft10::data_stream::FetchHeader,
304        #[cfg(feature = "draft11")]
305        Draft11 => crate::draft11::data_stream::FetchHeader,
306        #[cfg(feature = "draft12")]
307        Draft12 => crate::draft12::data_stream::FetchHeader,
308        #[cfg(feature = "draft13")]
309        Draft13 => crate::draft13::data_stream::FetchHeader,
310        #[cfg(feature = "draft14")]
311        Draft14 => crate::draft14::data_stream::FetchHeader,
312        #[cfg(feature = "draft15")]
313        Draft15 => crate::draft15::data_stream::FetchHeader,
314        #[cfg(feature = "draft16")]
315        Draft16 => crate::draft16::data_stream::FetchHeader,
316        #[cfg(feature = "draft17")]
317        Draft17 => crate::draft17::data_stream::FetchHeader,
318    }
319    decode(decode);
320    encode(encode -> ());
321}