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