coap-handler-implementations 0.3.5

Simple implementations of CoAP handlers
Documentation
//! Implementation of a /.well-known/core resource
//!
//! This is a private module to retain flexibilty around the implementation (even though all types
//! need to be public as they are associated types).

use crate::wkc::{write_link_format, Reporting};
use coap_handler::Handler;
use coap_message::{MessageOption, MutableWritableMessage, ReadableMessage};

const LINK_FORMAT: u16 = 40;

/// Wrapper around a reporting handler that makes all reported resources discoverable at the path
/// `/.well-known/core`.
///
/// This can be constructed via [crate::ReportingHandlerBuilder::with_wkc()], typically after
/// having gathered all resource in one handler.
pub struct WellKnownCore<H: Reporting + Handler>(H);

impl<H: Reporting + Handler> WellKnownCore<H> {
    pub(crate) fn new(handler: H) -> Self {
        WellKnownCore(handler)
    }
}

pub enum WkcData<T> {
    Wkc {
        // easiest error indication...
        code: u8,
        // to be extended later to queries, content format and more
        block2: crate::helpers::Block2RequestData,
    },
    Other(T),
}

impl<H: Reporting + Handler> Handler for WellKnownCore<H> {
    type RequestData = WkcData<H::RequestData>;

    fn extract_request_data(&mut self, req: &impl ReadableMessage) -> Self::RequestData {
        use crate::option_processing::OptionsExt;

        let mut block2 = None;

        let mut pathmatch = 0;

        let mut accept: Option<u16> = None;

        let opts = req
            .options()
            .ignore_uri_host()
            .filter(|o| match o.number() {
                coap_numbers::option::ACCEPT if accept.is_none() => {
                    accept = o.value_uint();
                    // leave option around to fail later if conversion failed
                    accept.is_none()
                }
                _ => true,
            })
            // Because the resource definition of RFC6690 explicitly allows ignoring these arguments
            .ignore_uri_query()
            .take_block2(&mut block2)
            // Don't take this as template, this is quick & dirty
            .take_uri_path(|p| {
                pathmatch = match (pathmatch, p) {
                    (0, ".well-known") => 1,
                    (1, "core") => 2,
                    _ => -1,
                }
            });

        let remaining = opts.ignore_elective_others();

        let block2 = block2.unwrap_or_default();

        if pathmatch == 2 {
            let code;
            if remaining.is_err() {
                code = coap_numbers::code::BAD_OPTION;
            } else if accept.unwrap_or(LINK_FORMAT) != LINK_FORMAT {
                code = coap_numbers::code::NOT_ACCEPTABLE;
            } else {
                code = coap_numbers::code::CONTENT;
            }
            WkcData::Wkc { code, block2 }
        } else {
            WkcData::Other(self.0.extract_request_data(req))
        }
    }
    fn estimate_length(&mut self, req: &Self::RequestData) -> usize {
        match req {
            // FIXME precision, consider requester block size
            WkcData::Wkc { .. } => 1024,
            WkcData::Other(req) => self.0.estimate_length(req),
        }
    }
    fn build_response(&mut self, m: &mut impl MutableWritableMessage, req: Self::RequestData) {
        match req {
            WkcData::Wkc { code, block2 } => {
                m.set_code(crate::helpers::codeconvert(code));
                if code == coap_numbers::code::CONTENT {
                    crate::helpers::block2_write_with_cf(
                        block2,
                        m,
                        |w| {
                            write_link_format(w, &self.0, &[]).expect("Block writers do not err.");
                        },
                        Some(40),
                    );
                } else {
                    m.truncate(0);
                }
            }
            WkcData::Other(req) => self.0.build_response(m, req),
        }
    }
}

/// For reporting, a [WellKnownCore] handler simply passes on its inner description; the exposed
/// .well-known/core resource is not listed.
///
/// Utilizing this (when in having a WellKnownCore handler around the resources, then adding more
/// resources using [`HandlerBuilder::at()`](crate::HandlerBuilder::at()), and wrapping the result in a WellKnownCore again) is
/// wasteful in terms of computation (as the inner one will never be hit), but can be convenient
/// for composition in examples.
impl<H: Reporting + Handler> Reporting for WellKnownCore<H> {
    type Record<'res> = H::Record<'res>
    where
        H: 'res,
    ;
    type Reporter<'res> = H::Reporter<'res>
    where
        H: 'res,
    ;

    fn report(&self) -> Self::Reporter<'_> {
        self.0.report()
    }
}