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    }
121    decode(decode);
122    encode(encode -> Result<(), CodecError>);
123}
124
125impl AnyControlMessage {
126    /// Returns `true` if this is a CLIENT_SETUP or SERVER_SETUP message.
127    pub fn is_setup(&self) -> bool {
128        match self {
129            #[cfg(feature = "draft07")]
130            AnyControlMessage::Draft07(m) => matches!(
131                m,
132                crate::draft07::message::ControlMessage::ClientSetup(_)
133                    | crate::draft07::message::ControlMessage::ServerSetup(_)
134            ),
135            #[cfg(feature = "draft08")]
136            AnyControlMessage::Draft08(m) => matches!(
137                m,
138                crate::draft08::message::ControlMessage::ClientSetup(_)
139                    | crate::draft08::message::ControlMessage::ServerSetup(_)
140            ),
141            #[cfg(feature = "draft09")]
142            AnyControlMessage::Draft09(m) => matches!(
143                m,
144                crate::draft09::message::ControlMessage::ClientSetup(_)
145                    | crate::draft09::message::ControlMessage::ServerSetup(_)
146            ),
147            #[cfg(feature = "draft10")]
148            AnyControlMessage::Draft10(m) => matches!(
149                m,
150                crate::draft10::message::ControlMessage::ClientSetup(_)
151                    | crate::draft10::message::ControlMessage::ServerSetup(_)
152            ),
153            #[cfg(feature = "draft11")]
154            AnyControlMessage::Draft11(m) => matches!(
155                m,
156                crate::draft11::message::ControlMessage::ClientSetup(_)
157                    | crate::draft11::message::ControlMessage::ServerSetup(_)
158            ),
159            #[cfg(feature = "draft12")]
160            AnyControlMessage::Draft12(m) => matches!(
161                m,
162                crate::draft12::message::ControlMessage::ClientSetup(_)
163                    | crate::draft12::message::ControlMessage::ServerSetup(_)
164            ),
165            #[cfg(feature = "draft13")]
166            AnyControlMessage::Draft13(m) => matches!(
167                m,
168                crate::draft13::message::ControlMessage::ClientSetup(_)
169                    | crate::draft13::message::ControlMessage::ServerSetup(_)
170            ),
171            #[cfg(feature = "draft14")]
172            AnyControlMessage::Draft14(m) => matches!(
173                m,
174                crate::draft14::message::ControlMessage::ClientSetup(_)
175                    | crate::draft14::message::ControlMessage::ServerSetup(_)
176            ),
177            #[cfg(feature = "draft15")]
178            AnyControlMessage::Draft15(m) => matches!(
179                m,
180                crate::draft15::message::ControlMessage::ClientSetup(_)
181                    | crate::draft15::message::ControlMessage::ServerSetup(_)
182            ),
183            #[cfg(feature = "draft16")]
184            AnyControlMessage::Draft16(m) => matches!(
185                m,
186                crate::draft16::message::ControlMessage::ClientSetup(_)
187                    | crate::draft16::message::ControlMessage::ServerSetup(_)
188            ),
189            #[cfg(feature = "draft17")]
190            AnyControlMessage::Draft17(m) => {
191                matches!(m, crate::draft17::message::ControlMessage::Setup(_))
192            }
193            #[cfg(feature = "draft18")]
194            AnyControlMessage::Draft18(m) => {
195                matches!(m, crate::draft18::message::ControlMessage::Setup(_))
196            }
197            #[allow(unreachable_patterns)]
198            _ => false,
199        }
200    }
201}
202
203// ── Data stream headers ─────────────────────────────────────
204
205dispatch_enum! {
206    /// A subgroup header from any enabled draft.
207    #[derive(Debug, Clone)]
208    pub enum AnySubgroupHeader {
209        #[cfg(feature = "draft07")]
210        Draft07 => crate::draft07::data_stream::SubgroupHeader,
211        #[cfg(feature = "draft08")]
212        Draft08 => crate::draft08::data_stream::SubgroupHeader,
213        #[cfg(feature = "draft09")]
214        Draft09 => crate::draft09::data_stream::SubgroupHeader,
215        #[cfg(feature = "draft10")]
216        Draft10 => crate::draft10::data_stream::SubgroupHeader,
217        #[cfg(feature = "draft11")]
218        Draft11 => crate::draft11::data_stream::SubgroupHeader,
219        #[cfg(feature = "draft12")]
220        Draft12 => crate::draft12::data_stream::SubgroupHeader,
221        #[cfg(feature = "draft13")]
222        Draft13 => crate::draft13::data_stream::SubgroupHeader,
223        #[cfg(feature = "draft14")]
224        Draft14 => crate::draft14::data_stream::SubgroupHeader,
225        #[cfg(feature = "draft15")]
226        Draft15 => crate::draft15::data_stream::SubgroupHeader,
227        #[cfg(feature = "draft16")]
228        Draft16 => crate::draft16::data_stream::SubgroupHeader,
229        #[cfg(feature = "draft17")]
230        Draft17 => crate::draft17::data_stream::SubgroupHeader,
231        #[cfg(feature = "draft18")]
232        Draft18 => crate::draft18::data_stream::SubgroupHeader,
233    }
234    decode(decode);
235    encode(encode -> ());
236}
237
238dispatch_enum! {
239    /// An object header from any enabled draft.
240    #[derive(Debug, Clone)]
241    pub enum AnyObjectHeader {
242        #[cfg(feature = "draft07")]
243        Draft07 => crate::draft07::data_stream::ObjectHeader,
244        #[cfg(feature = "draft08")]
245        Draft08 => crate::draft08::data_stream::ObjectHeader,
246        #[cfg(feature = "draft09")]
247        Draft09 => crate::draft09::data_stream::ObjectHeader,
248        #[cfg(feature = "draft10")]
249        Draft10 => crate::draft10::data_stream::ObjectHeader,
250        #[cfg(feature = "draft11")]
251        Draft11 => crate::draft11::data_stream::ObjectHeader,
252        #[cfg(feature = "draft12")]
253        Draft12 => crate::draft12::data_stream::ObjectHeader,
254        #[cfg(feature = "draft13")]
255        Draft13 => crate::draft13::data_stream::ObjectHeader,
256        // NOTE: drafts 14-17 have no standalone ObjectHeader. Subgroup
257        // objects on those drafts use delta-encoded object IDs and
258        // header-typed extension/properties presence flags, so
259        // decoding requires stateful per-stream context. Callers must
260        // use each draft's `SubgroupObjectReader` (or
261        // `FetchObject::decode` for fetch streams) directly.
262    }
263    decode(decode);
264    encode(encode -> ());
265}
266
267dispatch_enum! {
268    /// A datagram header from any enabled draft.
269    #[derive(Debug, Clone)]
270    pub enum AnyDatagramHeader {
271        #[cfg(feature = "draft07")]
272        Draft07 => crate::draft07::data_stream::DatagramHeader,
273        #[cfg(feature = "draft08")]
274        Draft08 => crate::draft08::data_stream::DatagramHeader,
275        #[cfg(feature = "draft09")]
276        Draft09 => crate::draft09::data_stream::DatagramHeader,
277        #[cfg(feature = "draft10")]
278        Draft10 => crate::draft10::data_stream::DatagramHeader,
279        #[cfg(feature = "draft11")]
280        Draft11 => crate::draft11::data_stream::DatagramHeader,
281        #[cfg(feature = "draft12")]
282        Draft12 => crate::draft12::data_stream::DatagramHeader,
283        #[cfg(feature = "draft13")]
284        Draft13 => crate::draft13::data_stream::DatagramHeader,
285        #[cfg(feature = "draft14")]
286        Draft14 => crate::draft14::data_stream::DatagramObject,
287        #[cfg(feature = "draft15")]
288        Draft15 => crate::draft15::data_stream::DatagramHeader,
289        #[cfg(feature = "draft16")]
290        Draft16 => crate::draft16::data_stream::DatagramHeader,
291        #[cfg(feature = "draft17")]
292        Draft17 => crate::draft17::data_stream::DatagramHeader,
293        #[cfg(feature = "draft18")]
294        Draft18 => crate::draft18::data_stream::DatagramHeader,
295    }
296    decode(decode);
297    encode(encode -> ());
298}
299
300dispatch_enum! {
301    /// A fetch header from any enabled draft.
302    ///
303    /// Note: Header structure varies significantly across drafts.
304    /// Draft-07 has a minimal fetch header, Draft-14 has a full header.
305    #[derive(Debug, Clone)]
306    pub enum AnyFetchHeader {
307        #[cfg(feature = "draft07")]
308        Draft07 => crate::draft07::data_stream::FetchHeader,
309        #[cfg(feature = "draft08")]
310        Draft08 => crate::draft08::data_stream::FetchHeader,
311        #[cfg(feature = "draft09")]
312        Draft09 => crate::draft09::data_stream::FetchHeader,
313        #[cfg(feature = "draft10")]
314        Draft10 => crate::draft10::data_stream::FetchHeader,
315        #[cfg(feature = "draft11")]
316        Draft11 => crate::draft11::data_stream::FetchHeader,
317        #[cfg(feature = "draft12")]
318        Draft12 => crate::draft12::data_stream::FetchHeader,
319        #[cfg(feature = "draft13")]
320        Draft13 => crate::draft13::data_stream::FetchHeader,
321        #[cfg(feature = "draft14")]
322        Draft14 => crate::draft14::data_stream::FetchHeader,
323        #[cfg(feature = "draft15")]
324        Draft15 => crate::draft15::data_stream::FetchHeader,
325        #[cfg(feature = "draft16")]
326        Draft16 => crate::draft16::data_stream::FetchHeader,
327        #[cfg(feature = "draft17")]
328        Draft17 => crate::draft17::data_stream::FetchHeader,
329        #[cfg(feature = "draft18")]
330        Draft18 => crate::draft18::data_stream::FetchHeader,
331    }
332    decode(decode);
333    encode(encode -> ());
334}