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}