rtc_interceptor/
registry.rs

1//! Interceptor Registry - Type-safe builder for constructing interceptor chains.
2//!
3//! The [`Registry`] provides a fluent API for composing interceptor chains. Each call
4//! to [`with()`](Registry::with) wraps the current chain with a new interceptor layer.
5//!
6//! # Chain Construction
7//!
8//! Interceptors are added from innermost to outermost. The first interceptor added
9//! becomes the innermost (closest to [`NoopInterceptor`](crate::NoopInterceptor)),
10//! and the last becomes the outermost (processes packets first).
11//!
12//! ```text
13//! Registry::new()
14//!     .with(InterceptorA)  // Innermost
15//!     .with(InterceptorB)  // Middle
16//!     .with(InterceptorC)  // Outermost
17//!     .build()
18//!
19//! Results in: C wraps B wraps A wraps NoopInterceptor
20//! ```
21
22use crate::Interceptor;
23use crate::noop::NoopInterceptor;
24
25/// Registry for constructing interceptor chains.
26///
27/// `Registry` wraps an interceptor chain and allows adding more interceptors
28/// via the [`with`](Registry::with) method. The chain can be extracted with [`build`](Registry::build).
29///
30/// # Example
31///
32/// ```ignore
33/// use rtc_interceptor::Registry;
34///
35/// // Create a new registry
36/// let mut registry = Registry::new();
37///
38/// // Add interceptors (can be done in helper functions)
39/// registry = registry
40///     .with(SenderReportBuilder::new().build())
41///     .with(ReceiverReportBuilder::new().build());
42///
43/// // Build the final chain
44/// let chain = registry.build();
45/// ```
46///
47/// # Helper Function Pattern
48///
49/// ```ignore
50/// fn register_default_interceptors<P: Interceptor>(
51///     registry: Registry<P>,
52/// ) -> Registry<impl Interceptor> {
53///     registry
54///         .with(SenderReportBuilder::new().build())
55///         .with(ReceiverReportBuilder::new().build())
56/// }
57///
58/// let registry = Registry::new();
59/// let registry = register_default_interceptors(registry);
60/// let chain = registry.build();
61/// ```
62#[derive(Clone)]
63pub struct Registry<P> {
64    inner: P,
65}
66
67impl Registry<NoopInterceptor> {
68    /// Create a new empty registry.
69    ///
70    /// This creates a `NoopInterceptor` as the innermost layer.
71    ///
72    /// # Example
73    ///
74    /// ```ignore
75    /// use rtc_interceptor::Registry;
76    ///
77    /// let registry = Registry::new();
78    /// ```
79    pub fn new() -> Self {
80        Registry {
81            inner: NoopInterceptor::new(),
82        }
83    }
84}
85
86impl Default for Registry<NoopInterceptor> {
87    fn default() -> Self {
88        Self::new()
89    }
90}
91
92impl<P: Interceptor> Registry<P> {
93    /// Create a registry from an existing interceptor.
94    ///
95    /// # Example
96    ///
97    /// ```ignore
98    /// let custom = MyCustomInterceptor::new();
99    /// let registry = Registry::from(custom);
100    /// ```
101    pub fn from(inner: P) -> Self {
102        Registry { inner }
103    }
104
105    /// Wrap the current chain with another interceptor.
106    ///
107    /// Returns a new `Registry` with the updated chain type.
108    ///
109    /// # Example
110    ///
111    /// ```ignore
112    /// let registry = Registry::new()
113    ///     .with(SenderReportBuilder::new().build())
114    ///     .with(ReceiverReportBuilder::new().build());
115    /// ```
116    pub fn with<O, F>(self, f: F) -> Registry<O>
117    where
118        F: FnOnce(P) -> O,
119        O: Interceptor,
120    {
121        Registry {
122            inner: f(self.inner),
123        }
124    }
125
126    /// Build and return the interceptor chain.
127    ///
128    /// Consumes the registry and returns the inner interceptor chain.
129    ///
130    /// # Example
131    ///
132    /// ```ignore
133    /// let registry = Registry::new().with(MyInterceptor::new);
134    /// let chain = registry.build();
135    /// ```
136    pub fn build(self) -> P {
137        self.inner
138    }
139}
140
141#[cfg(test)]
142mod tests {
143    use super::*;
144    use crate::TaggedPacket;
145    use sansio::Protocol;
146    use shared::error::Error;
147    use std::time::Instant;
148
149    fn dummy_rtp_packet() -> TaggedPacket {
150        TaggedPacket {
151            now: Instant::now(),
152            transport: Default::default(),
153            message: crate::Packet::Rtp(rtp::Packet::default()),
154        }
155    }
156
157    // A simple test interceptor that wraps an inner protocol
158    struct TestInterceptor<P> {
159        inner: P,
160        name: &'static str,
161    }
162
163    impl<P> TestInterceptor<P> {
164        fn new(inner: P) -> Self {
165            Self {
166                inner,
167                name: "test",
168            }
169        }
170
171        fn with_name(name: &'static str) -> impl FnOnce(P) -> Self {
172            move |inner| Self { inner, name }
173        }
174    }
175
176    impl<P: Interceptor> Protocol<TaggedPacket, TaggedPacket, ()> for TestInterceptor<P> {
177        type Rout = TaggedPacket;
178        type Wout = TaggedPacket;
179        type Eout = ();
180        type Error = Error;
181        type Time = Instant;
182
183        fn handle_read(&mut self, msg: TaggedPacket) -> Result<(), Self::Error> {
184            self.inner.handle_read(msg)
185        }
186
187        fn poll_read(&mut self) -> Option<Self::Rout> {
188            self.inner.poll_read()
189        }
190
191        fn handle_write(&mut self, msg: TaggedPacket) -> Result<(), Self::Error> {
192            self.inner.handle_write(msg)
193        }
194
195        fn poll_write(&mut self) -> Option<Self::Wout> {
196            self.inner.poll_write()
197        }
198    }
199
200    impl<P: Interceptor> Interceptor for TestInterceptor<P> {
201        fn bind_local_stream(&mut self, info: &crate::StreamInfo) {
202            self.inner.bind_local_stream(info);
203        }
204        fn unbind_local_stream(&mut self, info: &crate::StreamInfo) {
205            self.inner.unbind_local_stream(info);
206        }
207        fn bind_remote_stream(&mut self, info: &crate::StreamInfo) {
208            self.inner.bind_remote_stream(info);
209        }
210        fn unbind_remote_stream(&mut self, info: &crate::StreamInfo) {
211            self.inner.unbind_remote_stream(info);
212        }
213    }
214
215    #[test]
216    fn test_registry_new() {
217        let registry = Registry::new();
218        let mut chain = registry.build();
219        let pkt = dummy_rtp_packet();
220        chain.handle_read(pkt).unwrap();
221        assert!(chain.poll_read().is_none());
222    }
223
224    #[test]
225    fn test_registry_with_single_interceptor() {
226        let registry = Registry::new().with(TestInterceptor::new);
227        let mut chain = registry.build();
228
229        let pkt = dummy_rtp_packet();
230        chain.handle_read(pkt).unwrap();
231        assert!(chain.poll_read().is_none());
232        assert_eq!(chain.name, "test");
233    }
234
235    #[test]
236    fn test_registry_with_multiple_interceptors() {
237        let registry = Registry::new()
238            .with(TestInterceptor::with_name("inner"))
239            .with(TestInterceptor::with_name("outer"));
240        let mut chain = registry.build();
241
242        let pkt = dummy_rtp_packet();
243        chain.handle_read(pkt).unwrap();
244        assert!(chain.poll_read().is_none());
245        assert_eq!(chain.name, "outer");
246        assert_eq!(chain.inner.name, "inner");
247    }
248
249    #[test]
250    fn test_registry_from_inner() {
251        let custom = NoopInterceptor::new();
252        let registry = Registry::from(custom).with(TestInterceptor::new);
253        let mut chain = registry.build();
254
255        let pkt = dummy_rtp_packet();
256        let pkt_message = pkt.message.clone();
257        chain.handle_write(pkt).unwrap();
258        assert_eq!(chain.poll_write().unwrap().message, pkt_message);
259    }
260
261    // Test the helper function pattern
262    fn register_test_interceptors<P: Interceptor>(
263        registry: Registry<P>,
264    ) -> Registry<TestInterceptor<TestInterceptor<P>>> {
265        registry
266            .with(TestInterceptor::with_name("first"))
267            .with(TestInterceptor::with_name("second"))
268    }
269
270    #[test]
271    fn test_helper_function_pattern() {
272        let registry = Registry::new();
273        let registry = register_test_interceptors(registry);
274        let mut chain = registry.build();
275
276        let pkt = dummy_rtp_packet();
277        chain.handle_read(pkt).unwrap();
278        assert!(chain.poll_read().is_none());
279        assert_eq!(chain.name, "second");
280        assert_eq!(chain.inner.name, "first");
281    }
282}