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    MessageOption, MinimalWritableMessage, MutableWritableMessage, ReadableMessage,
6    error::RenderableOnMinimal,
7};
8use coap_numbers::option;
9
10use crate::forking_helpers::{ForkingRecord, PrefixedRecord};
11use crate::helpers::{MaskingUriUpToPath, MaskingUriUpToPathN};
12use crate::{NeverFound, wkc, wkc_implementation};
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    fn with_extra_record(
163        self,
164        target: &'a str,
165        attributes: &'a [Attribute],
166    ) -> WithExtraRecord<'a, Self> {
167        WithExtraRecord::new(self, target, attributes)
168    }
169}
170
171impl<OldRD, OldH> ReportingHandlerBuilder<'_, OldRD> for OldH
172where
173    OldH: Handler<RequestData = OldRD> + Reporting,
174{
175    // Methods are provided
176}
177
178pub struct ForkingHandler<'a, H1, H2> {
179    h1: H1,
180    h2: H2,
181
182    // I'd like to have a closure in here, and that'd almost work as a type D: Fn(&Message<Bin>)
183    // -> bool, but I can't write at()... -> ForkingHandler<impl ..., H, Self> in the trait's
184    // signature.
185    h1_condition: &'a [&'a str],
186}
187
188/// Tagged-union container for ForkingHandler
189#[derive(Debug)]
190pub enum ForkingRequestData<RD1, RD2> {
191    First(RD1),
192    Second(RD2),
193}
194
195impl<RE1, RE2> RenderableOnMinimal for ForkingRequestData<RE1, RE2>
196where
197    RE1: RenderableOnMinimal + core::fmt::Debug,
198    RE2: RenderableOnMinimal + core::fmt::Debug,
199{
200    type Error<IE: RenderableOnMinimal + core::fmt::Debug> = ForkingRequestData<
201        <RE1 as RenderableOnMinimal>::Error<IE>,
202        <RE2 as RenderableOnMinimal>::Error<IE>,
203    >;
204
205    fn render<M: MinimalWritableMessage>(
206        self,
207        message: &mut M,
208    ) -> Result<(), Self::Error<M::UnionError>> {
209        use ForkingRequestData::*;
210        match self {
211            First(e) => e.render(message).map_err(First),
212            Second(e) => e.render(message).map_err(Second),
213        }
214    }
215}
216
217impl<RD1, H1, RD2, H2> Handler for ForkingHandler<'_, H1, H2>
218where
219    H1: Handler<RequestData = RD1>,
220    H2: Handler<RequestData = RD2>,
221{
222    type RequestData = ForkingRequestData<RD1, RD2>;
223
224    type ExtractRequestError = ForkingRequestData<H1::ExtractRequestError, H2::ExtractRequestError>;
225    type BuildResponseError<M: MinimalWritableMessage> =
226        ForkingRequestData<H1::BuildResponseError<M>, H2::BuildResponseError<M>>;
227
228    fn extract_request_data<M: ReadableMessage>(
229        &mut self,
230        request: &M,
231    ) -> Result<Self::RequestData, Self::ExtractRequestError> {
232        let expected_path = self.h1_condition.iter().map(|s| s.as_bytes());
233        let actual_path = request.options().filter(|o| o.number() == option::URI_PATH);
234
235        Ok(
236            if IterOrderByBackport::cmp_by(expected_path, actual_path, |e, a| e.cmp(a.value()))
237                == core::cmp::Ordering::Equal
238            {
239                let masked = MaskingUriUpToPath(request);
240                ForkingRequestData::First(
241                    self.h1
242                        .extract_request_data(&masked)
243                        .map_err(ForkingRequestData::First)?,
244                )
245            } else {
246                ForkingRequestData::Second(
247                    self.h2
248                        .extract_request_data(request)
249                        .map_err(ForkingRequestData::Second)?,
250                )
251            },
252        )
253    }
254
255    fn estimate_length(&mut self, request: &Self::RequestData) -> usize {
256        match request {
257            ForkingRequestData::First(r) => self.h1.estimate_length(r),
258            ForkingRequestData::Second(r) => self.h2.estimate_length(r),
259        }
260    }
261
262    fn build_response<M: MutableWritableMessage>(
263        &mut self,
264        response: &mut M,
265        request: Self::RequestData,
266    ) -> Result<(), Self::BuildResponseError<M>> {
267        match request {
268            ForkingRequestData::First(r) => self
269                .h1
270                .build_response(response, r)
271                .map_err(ForkingRequestData::First)?,
272            ForkingRequestData::Second(r) => self
273                .h2
274                .build_response(response, r)
275                .map_err(ForkingRequestData::Second)?,
276        }
277        Ok(())
278    }
279}
280
281impl<RD1, H1, RD2, H2> Reporting for ForkingHandler<'_, H1, H2>
282where
283    H1: Handler<RequestData = RD1> + Reporting,
284    H2: Handler<RequestData = RD2> + Reporting,
285{
286    // FIXME: This is copied over from ForkingTreeHandler (and stripped to the general not(feature
287    // = "_nontrivial_option_processing") case)
288    //
289    // As it is now it would appear to warrant the Forking{,Tree}Handler unification hinted at the
290    // ForkingTreeHandler definition. Not starting the deduplication (which would be warranted by
291    // the below behemoth) as this'll all look much slimmer again once type_alias_impl_trait is
292    // on by default.
293    //
294    // (After that, could be that it's warranted as it's now more than a few 4-liners, could be
295    // that not).
296
297    type Record<'b>
298        = ForkingRecord<PrefixedRecord<'b, H1::Record<'b>>, H2::Record<'b>>
299    where
300        Self: 'b;
301
302    type Reporter<'b>
303        = core::iter::Chain<
304        core::iter::Map<H2::Reporter<'b>, fn(H2::Record<'b>) -> Self::Record<'b>>,
305        core::iter::Map<
306            core::iter::Zip<H1::Reporter<'b>, core::iter::Repeat<&'b [&'b str]>>,
307            fn((H1::Record<'b>, &'b [&'b str])) -> Self::Record<'b>,
308        >,
309    >
310    where
311        Self: 'b;
312    fn report(&self) -> Self::Reporter<'_> {
313        fn first<'c, H1R, H2R>(
314            prefixed_prefix: (H1R, &'c [&'c str]),
315        ) -> ForkingRecord<PrefixedRecord<'c, H1R>, H2R> {
316            let (prefixed, prefix) = prefixed_prefix;
317            ForkingRecord::First(PrefixedRecord { prefix, prefixed })
318        }
319        self.h2
320            .report()
321            .map(ForkingRecord::Second as fn(_) -> _)
322            .chain(
323                self.h1
324                    .report()
325                    .zip(core::iter::repeat(self.h1_condition))
326                    .map(first as fn(_) -> _),
327            )
328    }
329
330    fn write_extra_link_format(
331        &self,
332        writer: &mut impl core::fmt::Write,
333        is_first: &mut bool,
334    ) -> core::fmt::Result {
335        self.h1.write_extra_link_format(writer, is_first)?;
336        self.h2.write_extra_link_format(writer, is_first)?;
337        Ok(())
338    }
339}
340
341// This is identical to the ForkingHandler in its structure -- just the matching behavior differs.
342// Even ForkingRequestData can be shared; unfortunately, the main code is still duplicated -- it
343// could be refactored, but is it worth it for the identical 4-lines parts?
344pub struct ForkingTreeHandler<'a, H1, H2> {
345    h1: H1,
346    h2: H2,
347
348    h1_condition: &'a [&'a str],
349}
350
351impl<RD1, H1, RD2, H2> Handler for ForkingTreeHandler<'_, H1, H2>
352where
353    H1: Handler<RequestData = RD1>,
354    H2: Handler<RequestData = RD2>,
355{
356    type RequestData = ForkingRequestData<RD1, RD2>;
357
358    type ExtractRequestError = ForkingRequestData<H1::ExtractRequestError, H2::ExtractRequestError>;
359    type BuildResponseError<M: MinimalWritableMessage> =
360        ForkingRequestData<H1::BuildResponseError<M>, H2::BuildResponseError<M>>;
361
362    fn extract_request_data<M: ReadableMessage>(
363        &mut self,
364        request: &M,
365    ) -> Result<Self::RequestData, Self::ExtractRequestError> {
366        use ForkingRequestData::*;
367
368        let expected_path = self.h1_condition.iter().map(|s| s.as_bytes());
369        let actual_path = request
370            .options()
371            .filter(|o| o.number() == option::URI_PATH)
372            .take(self.h1_condition.len());
373
374        if IterOrderByBackport::cmp_by(expected_path, actual_path, |e, a| e.cmp(a.value()))
375            == core::cmp::Ordering::Equal
376        {
377            let masked = MaskingUriUpToPathN::new(request, self.h1_condition.len());
378            self.h1
379                .extract_request_data(&masked)
380                .map(First)
381                .map_err(First)
382        } else {
383            self.h2
384                .extract_request_data(request)
385                .map(Second)
386                .map_err(Second)
387        }
388    }
389
390    fn estimate_length(&mut self, request: &Self::RequestData) -> usize {
391        match request {
392            ForkingRequestData::First(r) => self.h1.estimate_length(r),
393            ForkingRequestData::Second(r) => self.h2.estimate_length(r),
394        }
395    }
396
397    fn build_response<M: MutableWritableMessage>(
398        &mut self,
399        response: &mut M,
400        request: Self::RequestData,
401    ) -> Result<(), Self::BuildResponseError<M>> {
402        use ForkingRequestData::*;
403        match request {
404            First(r) => self.h1.build_response(response, r).map_err(First),
405            Second(r) => self.h2.build_response(response, r).map_err(Second),
406        }
407    }
408}
409
410impl<RD1, H1, RD2, H2> Reporting for ForkingTreeHandler<'_, H1, H2>
411where
412    H1: Handler<RequestData = RD1> + Reporting,
413    H2: Handler<RequestData = RD2> + Reporting,
414{
415    type Record<'b>
416        = ForkingRecord<PrefixedRecord<'b, H1::Record<'b>>, H2::Record<'b>>
417    where
418        Self: 'b;
419
420    // FIXME: one of these is copied over to Reporting for ForkingHandler, see there.
421
422    #[cfg(feature = "_nontrivial_option_processing")]
423    type Reporter<'b>
424        = impl Iterator<Item = Self::Record<'b>>
425    where
426        Self: 'b;
427
428    #[cfg(feature = "_nontrivial_option_processing")]
429    fn report(&self) -> Self::Reporter<'_> {
430        self.h2
431            .report()
432            .map(ForkingRecord::Second as fn(_) -> _)
433            .chain(self.h1.report().map(|f| {
434                ForkingRecord::First(PrefixedRecord {
435                    prefix: self.h1_condition,
436                    prefixed: f,
437                })
438            }))
439    }
440
441    #[cfg(not(feature = "_nontrivial_option_processing"))]
442    type Reporter<'b>
443        = core::iter::Chain<
444        core::iter::Map<H2::Reporter<'b>, fn(H2::Record<'b>) -> Self::Record<'b>>,
445        core::iter::Map<
446            core::iter::Zip<H1::Reporter<'b>, core::iter::Repeat<&'b [&'b str]>>,
447            fn((H1::Record<'b>, &'b [&'b str])) -> Self::Record<'b>,
448        >,
449    >
450    where
451        Self: 'b;
452    #[cfg(not(feature = "_nontrivial_option_processing"))]
453    fn report(&self) -> Self::Reporter<'_> {
454        fn first<'c, H1R, H2R>(
455            prefixed_prefix: (H1R, &'c [&'c str]),
456        ) -> ForkingRecord<PrefixedRecord<'c, H1R>, H2R> {
457            let (prefixed, prefix) = prefixed_prefix;
458            ForkingRecord::First(PrefixedRecord { prefix, prefixed })
459        }
460        self.h2
461            .report()
462            .map(ForkingRecord::Second as fn(_) -> _)
463            .chain(
464                self.h1
465                    .report()
466                    .zip(core::iter::repeat(self.h1_condition))
467                    .map(first as fn(_) -> _),
468            )
469    }
470}
471
472pub struct WithExtraRecord<'a, H> {
473    handler: H,
474    target: &'a str,
475    attributes: &'a [Attribute],
476}
477
478impl<'a, H> WithExtraRecord<'a, H> {
479    pub fn new(handler: H, target: &'a str, attributes: &'a [Attribute]) -> Self {
480        Self {
481            handler,
482            target,
483            attributes,
484        }
485    }
486}
487
488// Plain forwarding implementation
489impl<H: Handler> Handler for WithExtraRecord<'_, H> {
490    type RequestData = H::RequestData;
491    type ExtractRequestError = H::ExtractRequestError;
492    type BuildResponseError<M: MinimalWritableMessage> = H::BuildResponseError<M>;
493
494    fn extract_request_data<M: ReadableMessage>(
495        &mut self,
496        request: &M,
497    ) -> Result<Self::RequestData, Self::ExtractRequestError> {
498        self.handler.extract_request_data(request)
499    }
500
501    fn estimate_length(&mut self, request: &Self::RequestData) -> usize {
502        self.handler.estimate_length(request)
503    }
504
505    fn build_response<M: MutableWritableMessage>(
506        &mut self,
507        response: &mut M,
508        request: Self::RequestData,
509    ) -> Result<(), Self::BuildResponseError<M>> {
510        self.handler.build_response(response, request)
511    }
512}
513
514impl<'a, H: Reporting> Reporting for WithExtraRecord<'a, H> {
515    type Record<'b>
516        = H::Record<'b>
517    where
518        Self: 'b;
519
520    type Reporter<'b>
521        = H::Reporter<'b>
522    where
523        Self: 'b;
524    fn report(&self) -> Self::Reporter<'_> {
525        self.handler.report()
526    }
527
528    fn write_extra_link_format(
529        &self,
530        writer: &mut impl core::fmt::Write,
531        is_first: &mut bool,
532    ) -> core::fmt::Result {
533        self.handler.write_extra_link_format(writer, is_first)?;
534
535        if !*is_first {
536            write!(writer, ",")?;
537        } else {
538            *is_first = false;
539        }
540
541        write!(writer, "<{}>", self.target)?;
542        for a in self.attributes {
543            a.write_link_format(writer)?;
544        }
545
546        Ok(())
547    }
548}