Skip to main content

moqtap_client/
dispatch.rs

1//! Unified multi-draft entry-point types.
2//!
3//! This module is the facade downstream consumers (e.g. a CLI or desktop app)
4//! use to hold a MoQT connection without caring which draft was negotiated.
5//! It mirrors [`moqtap_codec::dispatch`]: one enum variant per enabled draft,
6//! gated on its feature flag.
7//!
8//! Three types live here:
9//!
10//! - `AnyConnection` — wraps a draft-specific `Connection`.
11//! - `AnyClientEvent` — wraps a draft-specific `ClientEvent`.
12//! - `AnyConnectionObserver` — a trait that receives `AnyClientEvent`s.
13//!   Attached to an `AnyConnection` via `AnyConnection::set_observer`,
14//!   which installs a per-draft adapter on the inner connection.
15//!
16//! Draft-specific protocol methods (e.g. `subscribe`, `fetch`) are not on
17//! `AnyConnection` because their signatures differ across drafts — match
18//! on the variant to reach them.
19
20use std::sync::Arc;
21
22use moqtap_codec::version::DraftVersion;
23
24/// Generates the `AnyConnection` and `AnyClientEvent` enums plus the per-draft
25/// observer adapter, with one variant per enabled draft feature.
26macro_rules! dispatch_all {
27    (
28        $(
29            #[cfg(feature = $feat:literal)]
30            $variant:ident => $module:ident,
31        )+
32    ) => {
33        /// A MoQT client connection of any enabled draft version.
34        ///
35        /// Wraps the draft-specific `Connection` type. Methods common to all
36        /// drafts are forwarded; for draft-specific protocol calls, match on
37        /// the variant.
38        pub enum AnyConnection {
39            $(
40                #[cfg(feature = $feat)]
41                #[doc = concat!("A draft-", $feat, " connection.")]
42                $variant(crate::$module::connection::Connection),
43            )+
44        }
45
46        impl AnyConnection {
47            /// Returns the draft version this connection is using.
48            #[allow(unreachable_code)]
49            pub fn draft(&self) -> DraftVersion {
50                match self {
51                    $(
52                        #[cfg(feature = $feat)]
53                        Self::$variant(_) => DraftVersion::$variant,
54                    )+
55                    #[allow(unreachable_patterns)]
56                    _ => unreachable!("AnyConnection has no enabled variants"),
57                }
58            }
59
60            /// Attach an observer. The observer is adapted into the
61            /// draft-specific observer trait and installed on the inner
62            /// connection; events are forwarded as [`AnyClientEvent`].
63            ///
64            /// Replaces any previously attached observer.
65            #[allow(unused_variables)]
66            pub fn set_observer(&mut self, observer: Arc<dyn AnyConnectionObserver>) {
67                match self {
68                    $(
69                        #[cfg(feature = $feat)]
70                        Self::$variant(c) => {
71                            c.set_observer(Box::new($variant::Adapter(observer)));
72                        }
73                    )+
74                    #[allow(unreachable_patterns)]
75                    _ => {}
76                }
77            }
78
79            /// Remove any attached observer.
80            pub fn clear_observer(&mut self) {
81                match self {
82                    $(
83                        #[cfg(feature = $feat)]
84                        Self::$variant(c) => c.clear_observer(),
85                    )+
86                    #[allow(unreachable_patterns)]
87                    _ => {}
88                }
89            }
90
91            /// Close the connection with the given application error code
92            /// and reason.
93            #[allow(unused_variables)]
94            pub fn close(&self, code: u32, reason: &[u8]) {
95                match self {
96                    $(
97                        #[cfg(feature = $feat)]
98                        Self::$variant(c) => c.close(code, reason),
99                    )+
100                    #[allow(unreachable_patterns)]
101                    _ => {}
102                }
103            }
104        }
105
106        /// An event from a MoQT connection of any enabled draft version.
107        ///
108        /// Event shapes differ across drafts (e.g. draft-17's
109        /// `SubgroupObjectReceived` carries header types, while earlier
110        /// drafts carry decoded objects). Match on the variant to inspect
111        /// the draft-specific event.
112        #[non_exhaustive]
113        #[derive(Debug, Clone)]
114        pub enum AnyClientEvent {
115            $(
116                #[cfg(feature = $feat)]
117                #[doc = concat!("A draft-", $feat, " event.")]
118                $variant(crate::$module::event::ClientEvent),
119            )+
120        }
121
122        impl AnyClientEvent {
123            /// Returns the draft version this event belongs to.
124            #[allow(unreachable_code)]
125            pub fn draft(&self) -> DraftVersion {
126                match self {
127                    $(
128                        #[cfg(feature = $feat)]
129                        Self::$variant(_) => DraftVersion::$variant,
130                    )+
131                    #[allow(unreachable_patterns)]
132                    _ => unreachable!("AnyClientEvent has no enabled variants"),
133                }
134            }
135        }
136
137        // Per-draft adapter modules. Each holds an `Adapter` struct that
138        // implements the draft's `ConnectionObserver` trait by forwarding to
139        // an `AnyConnectionObserver`.
140        $(
141            #[cfg(feature = $feat)]
142            #[allow(non_snake_case)]
143            mod $variant {
144                use super::{AnyClientEvent, AnyConnectionObserver};
145                use std::sync::Arc;
146
147                pub(super) struct Adapter(pub(super) Arc<dyn AnyConnectionObserver>);
148
149                impl crate::$module::observer::ConnectionObserver for Adapter {
150                    fn on_event(&self, event: &crate::$module::event::ClientEvent) {
151                        self.0.on_event(&AnyClientEvent::$variant(event.clone()));
152                    }
153
154                    fn on_event_owned(&self, event: crate::$module::event::ClientEvent) {
155                        self.0.on_event(&AnyClientEvent::$variant(event));
156                    }
157                }
158            }
159        )+
160    };
161}
162
163dispatch_all! {
164    #[cfg(feature = "draft07")]
165    Draft07 => draft07,
166    #[cfg(feature = "draft08")]
167    Draft08 => draft08,
168    #[cfg(feature = "draft09")]
169    Draft09 => draft09,
170    #[cfg(feature = "draft10")]
171    Draft10 => draft10,
172    #[cfg(feature = "draft11")]
173    Draft11 => draft11,
174    #[cfg(feature = "draft12")]
175    Draft12 => draft12,
176    #[cfg(feature = "draft13")]
177    Draft13 => draft13,
178    #[cfg(feature = "draft14")]
179    Draft14 => draft14,
180    #[cfg(feature = "draft15")]
181    Draft15 => draft15,
182    #[cfg(feature = "draft16")]
183    Draft16 => draft16,
184    #[cfg(feature = "draft17")]
185    Draft17 => draft17,
186}
187
188/// Trait for receiving events from an [`AnyConnection`].
189///
190/// Implementations must be `Send + Sync` because the adapter installed on
191/// the inner draft-specific connection may emit events from async tasks.
192/// `on_event` takes `&self` — implementations that need mutation should use
193/// interior mutability (e.g. `Mutex`, `mpsc::Sender`).
194///
195/// The per-draft adapter clones the draft-specific event into the matching
196/// [`AnyClientEvent`] variant before invoking `on_event`.
197pub trait AnyConnectionObserver: Send + Sync {
198    /// Called when a connection event occurs on any draft.
199    fn on_event(&self, event: &AnyClientEvent);
200}
201
202/// A no-op observer that discards all events.
203pub struct NoOpObserver;
204
205impl AnyConnectionObserver for NoOpObserver {
206    fn on_event(&self, _event: &AnyClientEvent) {}
207}