coap_handler_implementations/
forking.rs

1//! Module containing code for all the components for building trees of resources
2
3use coap_handler::{Attribute, Handler, Reporting};
4use coap_message::{
5    error::RenderableOnMinimal, MessageOption, MinimalWritableMessage, MutableWritableMessage,
6    ReadableMessage,
7};
8use coap_numbers::option;
9
10use crate::forking_helpers::{ForkingRecord, PrefixedRecord};
11use crate::helpers::{MaskingUriUpToPath, MaskingUriUpToPathN};
12use crate::{wkc, wkc_implementation, NeverFound};
13
14/// Backport of <https://github.com/rust-lang/rust/issues/64295>. The contested points about the
15/// API do not apply.
16trait IterOrderByBackport: Iterator {
17    fn cmp_by<I, F>(mut self, other: I, mut cmp: F) -> core::cmp::Ordering
18    where
19        Self: Sized,
20        I: IntoIterator,
21        F: FnMut(Self::Item, I::Item) -> core::cmp::Ordering,
22    {
23        let mut other = other.into_iter();
24
25        loop {
26            let x = match self.next() {
27                None => {
28                    if other.next().is_none() {
29                        return core::cmp::Ordering::Equal;
30                    } else {
31                        return core::cmp::Ordering::Less;
32                    }
33                }
34                Some(val) => val,
35            };
36
37            let y = match other.next() {
38                None => return core::cmp::Ordering::Greater,
39                Some(val) => val,
40            };
41
42            match cmp(x, y) {
43                core::cmp::Ordering::Equal => (),
44                non_eq => return non_eq,
45            }
46        }
47    }
48}
49
50impl<T: Iterator> IterOrderByBackport for T {}
51
52/// Start building a tree of sub-resources
53///
54/// While technically this just returns a handler that returns 4.04 unconditionally, it also
55/// implements HandlerBuilder, and thus can be used like this:
56///
57/// ```
58/// use coap_handler_implementations::*;
59/// let handler = new_dispatcher()
60///     .at(&["dev", "name"], SimpleRendered::new_typed_str("Demo program", Some(0)))
61///     .at(&["dev", "version"], SimpleRendered::new_typed_str("0.8.15", Some(0)))
62///     .with_wkc()
63///     ;
64/// ```
65#[cfg(not(feature = "leaky_names"))]
66pub fn new_dispatcher() -> impl Handler + Reporting {
67    wkc::NotReporting::new(NeverFound {})
68}
69#[cfg(feature = "leaky_names")]
70pub fn new_dispatcher() -> wkc::NotReporting<NeverFound> {
71    wkc::NotReporting::new(NeverFound {})
72}
73
74/// Trait implemented by default on all handlers that lets the user stack them using a builder-like
75/// syntax.
76///
77/// Note that the resulting ForkingRequestData<ForkingRequestData<...>,()> enums that might look
78/// wasteful on paper are optimized into the minimum necessary size since
79/// <https://github.com/rust-lang/rust/pull/45225>. They are, however, suboptimal when it comes to
80/// how many times the options are read.
81pub trait HandlerBuilder<'a, OldRD>
82where
83    Self: Handler + Sized,
84{
85    /// Divert requests arriving at `path` into the given `handler`.
86    ///
87    /// The handler will not *not* see the Uri-Path (and Uri-Host, as this builder doesn't do
88    /// virtual hosting yet) options any more; see the top-level module documentation on Options
89    /// Hiding for rationale.
90    ///
91    /// If both the previous tree and the new handler are Reporting, so is the result.
92    fn at<H>(self, path: &'a [&'a str], handler: H) -> ForkingHandler<'a, H, Self>
93    where
94        H: Handler + Sized,
95    {
96        ForkingHandler {
97            h1: handler,
98            h2: self,
99            h1_condition: path,
100        }
101    }
102
103    /// Divert requests arriving at `path` into the given `handler`, and announce them with the
104    /// given attributes in .well-known/core.
105    ///
106    /// Any reporting the handler would have done is overridden.
107    ///
108    /// This is a shorthand for `.at(ConstantSingleRecordReport::new(h, attributes))`.
109    fn at_with_attributes<H>(
110        self,
111        path: &'a [&'a str],
112        attributes: &'a [Attribute],
113        handler: H,
114    ) -> ForkingHandler<'a, wkc::ConstantSingleRecordReport<'a, H>, Self>
115    where
116        H: Handler + Sized,
117    {
118        ForkingHandler {
119            h1: wkc::ConstantSingleRecordReport::new(handler, attributes),
120            h2: self,
121            h1_condition: path,
122        }
123    }
124
125    /// Divert requests arriving with an Uri-Path starting with `path` to the given `handler`.
126    ///
127    /// Only remaining Uri-Path options will be visible to the handler; those expressed in path
128    /// (and Uri-Host, see [.at()]) are hidden.
129    ///
130    /// If both the previous tree and the new handler are Reporting, so is the result.
131    fn below<H>(self, path: &'a [&'a str], handler: H) -> ForkingTreeHandler<'a, H, Self> {
132        ForkingTreeHandler {
133            h1: handler,
134            h2: self,
135            h1_condition: path,
136        }
137    }
138}
139
140impl<OldRD, OldH> HandlerBuilder<'_, OldRD> for OldH
141where
142    Self: Handler<RequestData = OldRD> + Sized,
143{
144    // Methods are provided
145}
146
147/// Extension trait for handlers that also implement [Reporting](coap_handler::Reporting).
148///
149/// Like [HandlerBuilder] this is implemented for wherever it works.
150///
151/// (Note that while this *could* be implemented as a provided method of Reporting, it is split out
152/// to stay architecturally analogous to the HandlerBuilder, and to not force this crate's
153/// implementation of .well-known/core onto other users of Reporting. Possibly, these could be
154/// separated approaching stabilization.)
155pub trait ReportingHandlerBuilder<'a, OldRD>: HandlerBuilder<'a, OldRD> + Reporting {
156    /// Add a `/.well-known/core` resource that exposes the information the previous (stack of)
157    /// handler(s) exposes throught the [Reporting](coap_handler::Reporting) trait.
158    fn with_wkc(self) -> wkc_implementation::WellKnownCore<Self> {
159        wkc_implementation::WellKnownCore::new(self)
160    }
161}
162
163impl<OldRD, OldH> ReportingHandlerBuilder<'_, OldRD> for OldH
164where
165    OldH: Handler<RequestData = OldRD> + Reporting,
166{
167    // Methods are provided
168}
169
170pub struct ForkingHandler<'a, H1, H2> {
171    h1: H1,
172    h2: H2,
173
174    // I'd like to have a closure in here, and that'd almost work as a type D: Fn(&Message<Bin>)
175    // -> bool, but I can't write at()... -> ForkingHandler<impl ..., H, Self> in the trait's
176    // signature.
177    h1_condition: &'a [&'a str],
178}
179
180/// Tagged-union container for ForkingHandler
181#[derive(Debug)]
182pub enum ForkingRequestData<RD1, RD2> {
183    First(RD1),
184    Second(RD2),
185}
186
187impl<RE1, RE2> RenderableOnMinimal for ForkingRequestData<RE1, RE2>
188where
189    RE1: RenderableOnMinimal + core::fmt::Debug,
190    RE2: RenderableOnMinimal + core::fmt::Debug,
191{
192    type Error<IE: RenderableOnMinimal + core::fmt::Debug> = ForkingRequestData<
193        <RE1 as RenderableOnMinimal>::Error<IE>,
194        <RE2 as RenderableOnMinimal>::Error<IE>,
195    >;
196
197    fn render<M: MinimalWritableMessage>(
198        self,
199        message: &mut M,
200    ) -> Result<(), Self::Error<M::UnionError>> {
201        use ForkingRequestData::*;
202        match self {
203            First(e) => e.render(message).map_err(First),
204            Second(e) => e.render(message).map_err(Second),
205        }
206    }
207}
208
209impl<RD1, H1, RD2, H2> Handler for ForkingHandler<'_, H1, H2>
210where
211    H1: Handler<RequestData = RD1>,
212    H2: Handler<RequestData = RD2>,
213{
214    type RequestData = ForkingRequestData<RD1, RD2>;
215
216    type ExtractRequestError = ForkingRequestData<H1::ExtractRequestError, H2::ExtractRequestError>;
217    type BuildResponseError<M: MinimalWritableMessage> =
218        ForkingRequestData<H1::BuildResponseError<M>, H2::BuildResponseError<M>>;
219
220    fn extract_request_data<M: ReadableMessage>(
221        &mut self,
222        request: &M,
223    ) -> Result<Self::RequestData, Self::ExtractRequestError> {
224        let expected_path = self.h1_condition.iter().map(|s| s.as_bytes());
225        let actual_path = request.options().filter(|o| o.number() == option::URI_PATH);
226
227        Ok(
228            if IterOrderByBackport::cmp_by(expected_path, actual_path, |e, a| e.cmp(a.value()))
229                == core::cmp::Ordering::Equal
230            {
231                let masked = MaskingUriUpToPath(request);
232                ForkingRequestData::First(
233                    self.h1
234                        .extract_request_data(&masked)
235                        .map_err(ForkingRequestData::First)?,
236                )
237            } else {
238                ForkingRequestData::Second(
239                    self.h2
240                        .extract_request_data(request)
241                        .map_err(ForkingRequestData::Second)?,
242                )
243            },
244        )
245    }
246
247    fn estimate_length(&mut self, request: &Self::RequestData) -> usize {
248        match request {
249            ForkingRequestData::First(r) => self.h1.estimate_length(r),
250            ForkingRequestData::Second(r) => self.h2.estimate_length(r),
251        }
252    }
253
254    fn build_response<M: MutableWritableMessage>(
255        &mut self,
256        response: &mut M,
257        request: Self::RequestData,
258    ) -> Result<(), Self::BuildResponseError<M>> {
259        match request {
260            ForkingRequestData::First(r) => self
261                .h1
262                .build_response(response, r)
263                .map_err(ForkingRequestData::First)?,
264            ForkingRequestData::Second(r) => self
265                .h2
266                .build_response(response, r)
267                .map_err(ForkingRequestData::Second)?,
268        }
269        Ok(())
270    }
271}
272
273impl<RD1, H1, RD2, H2> Reporting for ForkingHandler<'_, H1, H2>
274where
275    H1: Handler<RequestData = RD1> + Reporting,
276    H2: Handler<RequestData = RD2> + Reporting,
277{
278    // FIXME: This is copied over from ForkingTreeHandler (and stripped to the general not(feature
279    // = "_nontrivial_option_processing") case)
280    //
281    // As it is now it would appear to warrant the Forking{,Tree}Handler unification hinted at the
282    // ForkingTreeHandler definition. Not starting the deduplication (which would be warranted by
283    // the below behemoth) as this'll all look much slimmer again once type_alias_impl_trait is
284    // on by default.
285    //
286    // (After that, could be that it's warranted as it's now more than a few 4-liners, could be
287    // that not).
288
289    type Record<'b>
290        = ForkingRecord<PrefixedRecord<'b, H1::Record<'b>>, H2::Record<'b>>
291    where
292        Self: 'b;
293
294    type Reporter<'b>
295        = core::iter::Chain<
296        core::iter::Map<H2::Reporter<'b>, fn(H2::Record<'b>) -> Self::Record<'b>>,
297        core::iter::Map<
298            core::iter::Zip<H1::Reporter<'b>, core::iter::Repeat<&'b [&'b str]>>,
299            fn((H1::Record<'b>, &'b [&'b str])) -> Self::Record<'b>,
300        >,
301    >
302    where
303        Self: 'b;
304    fn report(&self) -> Self::Reporter<'_> {
305        fn first<'c, H1R, H2R>(
306            prefixed_prefix: (H1R, &'c [&'c str]),
307        ) -> ForkingRecord<PrefixedRecord<'c, H1R>, H2R> {
308            let (prefixed, prefix) = prefixed_prefix;
309            ForkingRecord::First(PrefixedRecord { prefix, prefixed })
310        }
311        self.h2
312            .report()
313            .map(ForkingRecord::Second as fn(_) -> _)
314            .chain(
315                self.h1
316                    .report()
317                    .zip(core::iter::repeat(self.h1_condition))
318                    .map(first as fn(_) -> _),
319            )
320    }
321}
322
323// This is identical to the ForkingHandler in its structure -- just the matching behavior differs.
324// Even ForkingRequestData can be shared; unfortunately, the main code is still duplicated -- it
325// could be refactored, but is it worth it for the identical 4-lines parts?
326pub struct ForkingTreeHandler<'a, H1, H2> {
327    h1: H1,
328    h2: H2,
329
330    h1_condition: &'a [&'a str],
331}
332
333impl<RD1, H1, RD2, H2> Handler for ForkingTreeHandler<'_, H1, H2>
334where
335    H1: Handler<RequestData = RD1>,
336    H2: Handler<RequestData = RD2>,
337{
338    type RequestData = ForkingRequestData<RD1, RD2>;
339
340    type ExtractRequestError = ForkingRequestData<H1::ExtractRequestError, H2::ExtractRequestError>;
341    type BuildResponseError<M: MinimalWritableMessage> =
342        ForkingRequestData<H1::BuildResponseError<M>, H2::BuildResponseError<M>>;
343
344    fn extract_request_data<M: ReadableMessage>(
345        &mut self,
346        request: &M,
347    ) -> Result<Self::RequestData, Self::ExtractRequestError> {
348        use ForkingRequestData::*;
349
350        let expected_path = self.h1_condition.iter().map(|s| s.as_bytes());
351        let actual_path = request
352            .options()
353            .filter(|o| o.number() == option::URI_PATH)
354            .take(self.h1_condition.len());
355
356        if IterOrderByBackport::cmp_by(expected_path, actual_path, |e, a| e.cmp(a.value()))
357            == core::cmp::Ordering::Equal
358        {
359            let masked = MaskingUriUpToPathN::new(request, self.h1_condition.len());
360            self.h1
361                .extract_request_data(&masked)
362                .map(First)
363                .map_err(First)
364        } else {
365            self.h2
366                .extract_request_data(request)
367                .map(Second)
368                .map_err(Second)
369        }
370    }
371
372    fn estimate_length(&mut self, request: &Self::RequestData) -> usize {
373        match request {
374            ForkingRequestData::First(r) => self.h1.estimate_length(r),
375            ForkingRequestData::Second(r) => self.h2.estimate_length(r),
376        }
377    }
378
379    fn build_response<M: MutableWritableMessage>(
380        &mut self,
381        response: &mut M,
382        request: Self::RequestData,
383    ) -> Result<(), Self::BuildResponseError<M>> {
384        use ForkingRequestData::*;
385        match request {
386            First(r) => self.h1.build_response(response, r).map_err(First),
387            Second(r) => self.h2.build_response(response, r).map_err(Second),
388        }
389    }
390}
391
392impl<RD1, H1, RD2, H2> Reporting for ForkingTreeHandler<'_, H1, H2>
393where
394    H1: Handler<RequestData = RD1> + Reporting,
395    H2: Handler<RequestData = RD2> + Reporting,
396{
397    type Record<'b>
398        = ForkingRecord<PrefixedRecord<'b, H1::Record<'b>>, H2::Record<'b>>
399    where
400        Self: 'b;
401
402    // FIXME: one of these is copied over to Reporting for ForkingHandler, see there.
403
404    #[cfg(feature = "_nontrivial_option_processing")]
405    type Reporter<'b>
406        = impl Iterator<Item = Self::Record<'b>>
407    where
408        Self: 'b;
409
410    #[cfg(feature = "_nontrivial_option_processing")]
411    fn report(&self) -> Self::Reporter<'_> {
412        self.h2
413            .report()
414            .map(ForkingRecord::Second as fn(_) -> _)
415            .chain(self.h1.report().map(|f| {
416                ForkingRecord::First(PrefixedRecord {
417                    prefix: self.h1_condition,
418                    prefixed: f,
419                })
420            }))
421    }
422
423    #[cfg(not(feature = "_nontrivial_option_processing"))]
424    type Reporter<'b>
425        = core::iter::Chain<
426        core::iter::Map<H2::Reporter<'b>, fn(H2::Record<'b>) -> Self::Record<'b>>,
427        core::iter::Map<
428            core::iter::Zip<H1::Reporter<'b>, core::iter::Repeat<&'b [&'b str]>>,
429            fn((H1::Record<'b>, &'b [&'b str])) -> Self::Record<'b>,
430        >,
431    >
432    where
433        Self: 'b;
434    #[cfg(not(feature = "_nontrivial_option_processing"))]
435    fn report(&self) -> Self::Reporter<'_> {
436        fn first<'c, H1R, H2R>(
437            prefixed_prefix: (H1R, &'c [&'c str]),
438        ) -> ForkingRecord<PrefixedRecord<'c, H1R>, H2R> {
439            let (prefixed, prefix) = prefixed_prefix;
440            ForkingRecord::First(PrefixedRecord { prefix, prefixed })
441        }
442        self.h2
443            .report()
444            .map(ForkingRecord::Second as fn(_) -> _)
445            .chain(
446                self.h1
447                    .report()
448                    .zip(core::iter::repeat(self.h1_condition))
449                    .map(first as fn(_) -> _),
450            )
451    }
452}