coap-message-implementations 0.1.11

Implementations of coap-message traits, and tools for building them
Documentation
//! Implementation of [`coap_message::ReadableMessage`] based on a serialized message
//!
//! [Message] is the main struct of this module -- if the message is available as a slice that
//! lives as long as the message, that's the type to use. Otherwise, implement [`EncodedMessage`] on
//! your data, and wrap it in an [`EncodedMessageView`]. (The [Message] is nothing else than the
//! options/payload slice plus the code in a struct).
//!
//! Note that the [`crate::inmemory_write`] has a similar mechanism but does without an equivalent
//! "view"; at the next breaking revision, those might be unified.
#![cfg_attr(feature = "downcast", allow(unsafe_code))]

use coap_message::{ReadableMessage, WithSortedOptions};

use crate::option_iteration::{OptItem, OptPayloadReader};

/// An iterator producing [`MessageOption`]s by running along an encoded CoAP message options stream
/// by using a [`OptPayloadReader`] and discarding the payload.
pub struct OptionsIter<'a>(pub(crate) OptPayloadReader<'a>);

/// Option value used by this library to indicate a format error in the message that was not
/// detected by the time option / payload processing was started.
///
/// As this option is critical and proxy-unsafe, no application can expect to successfully process
/// any message that contains this option. (The number is from the experimental-use range;
/// implementations using this module will simply not support that option in experiments, as they
/// can not distinguish it from an option formatting error).
pub const OPTION_INVALID: u16 = u16::MAX;

impl<'a> Iterator for OptionsIter<'a> {
    type Item = MessageOption<'a>;

    fn next(&mut self) -> Option<MessageOption<'a>> {
        match self.0.next() {
            Some(OptItem::Option { number, data }) => Some(MessageOption {
                number,
                value: data,
            }),
            Some(OptItem::Error { .. }) => Some(MessageOption {
                number: OPTION_INVALID,
                value: &[],
            }),
            // No need to recurse or loop -- it's always the last one
            Some(OptItem::Payload(_)) => None,
            None => None,
        }
    }
}

/// A simple [`coap_message::MessageOption`] implementation for memory-mapped CoAP messages
pub struct MessageOption<'a> {
    number: u16,
    value: &'a [u8],
}

impl coap_message::MessageOption for MessageOption<'_> {
    fn number(&self) -> u16 {
        self.number
    }
    fn value(&self) -> &[u8] {
        self.value
    }
}

/// A CoAP message that resides in contiguous readable memory
///
/// This implementation does not attempt to do any early validation. On encoding errors discovered
/// at runtime, it simply emits the critical-and-not-safe-to-forward CoAP option 65535
/// ([`OPTION_INVALID`]), which to the indication indicates that something went wrong
///
/// Message is covariant over its lifetime because it only reads from there. FIXME: It should
/// express that using a Deref or something like that.
///
/// # Implementation details
///
/// When used with [`coap_message::helpers::RefWithStaticType`], it always uses its `'static`
/// counterpart as the corresponding type ID. Its [`Self::downcast_from`] method uses this, and
/// restores the lifetime based on the given impl Trait reference's lifetime using its covariance
/// property.
#[derive(Clone, Debug)]
pub struct Message<'a> {
    inner: EncodedMessageView<SliceMessage<'a>>,
}

#[derive(Clone, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
struct SliceMessage<'a> {
    code: u8,
    options_and_payload: &'a [u8],
}

impl EncodedMessage for SliceMessage<'_> {
    fn code(&self) -> u8 {
        self.code
    }
    fn options_and_payload(&self) -> &[u8] {
        self.options_and_payload
    }
}

impl<'a> Message<'a> {
    #[must_use]
    pub fn new(code: u8, options_and_payload: &'a [u8]) -> Self {
        Self {
            inner: EncodedMessageView::new(SliceMessage {
                code,
                options_and_payload,
            }),
        }
    }

    #[cfg(feature = "downcast")]
    pub fn downcast_from<M: ReadableMessage>(generic: &'a M) -> Option<&'a Self> {
        let (reference, type_id) = generic.with_static_type_annotation()?.into_inner();
        if type_id != core::any::TypeId::of::<Message<'static>>() {
            return None;
        }
        // One of us, one of us.
        let ptr = reference as *const M as *const Self;
        // SAFETY: The RefWithStaticType matching our type ID will only be constructed if M is
        // Message, and whatever Message<'b> it was before, it will live at least for 'a (because
        // we got a &'a Message<'b> and is covariant.
        Some(unsafe { &*ptr })
    }
}

impl ReadableMessage for Message<'_> {
    type Code = u8;
    type MessageOption<'a>
        = MessageOption<'a>
    where
        Self: 'a;
    type OptionsIter<'a>
        = OptionsIter<'a>
    where
        Self: 'a;

    fn code(&self) -> u8 {
        self.inner.code()
    }

    fn payload(&self) -> &[u8] {
        self.inner.payload()
    }

    fn options(&self) -> OptionsIter<'_> {
        self.inner.options()
    }

    #[cfg(feature = "downcast")]
    fn with_static_type_annotation(
        &self,
    ) -> Option<coap_message::helpers::RefWithStaticType<'_, Self>> {
        // SAFETY: It is this type's policy that its RefWithStaticType ID is the given
        // Message<'static>.
        Some(unsafe {
            coap_message::helpers::RefWithStaticType::new(
                self,
                core::any::TypeId::of::<Message<'static>>(),
            )
        })
    }
}
impl WithSortedOptions for Message<'_> {}

/// A trait that can implemented on in-memory encoded messages; then, an [`EncodedMessageView`]
/// struct can be placed around the implementer to implement [`coap_message::ReadableMessage`].
///
/// Implementations must not alter the content returned by the accessors. As this trait is safe to
/// implement, the content of the returned slice could change between calls even when the encoded
/// message was owned or exclusively referenced (e.g. when backed by a
/// [`RefCell`][core::cell::RefCell]). Therefore, neither this crate nor others can rely on
/// the content upholding any safety guarantees -- but it may (and should) panic rater than erring
/// if the expectation of stable content is violated.
pub trait EncodedMessage {
    /// The code of the message
    fn code(&self) -> u8;
    /// The memory area containing the encoded options, payload marker and payload
    fn options_and_payload(&self) -> &[u8];
}

/// A wrapper around any data structure containing a readable message
///
/// This is not `Sync` -- while it is currently a zero-sized wrapper, it may gain optimizations
/// later such as memoizing where the payload begins (and this is only practical with interior
/// mutability).
///
/// See [Message] for how this does not perform early validation and handles message errors.
// FIXME test whether the _phantom does the right thing
#[derive(Clone, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct EncodedMessageView<EM: EncodedMessage> {
    inner: EM,
    _phantom: core::marker::PhantomData<core::cell::Cell<()>>,
}

impl<EM: EncodedMessage> EncodedMessageView<EM> {
    pub fn new(inner: EM) -> Self {
        Self {
            inner,
            _phantom: core::marker::PhantomData,
        }
    }
}

/// When the inner item stores more than just the code and encoded options, this can be used to
/// gain read-only access to any additional data. (Mutating access is blocked to ensure that future
/// optimizations like memoizing the payload position are possible; it might still be enabled if
/// memoized data is cleared just to be on the safe side).
// FIXME: Might even dereference into it?
impl<EM: EncodedMessage> AsRef<EM> for EncodedMessageView<EM> {
    fn as_ref(&self) -> &EM {
        &self.inner
    }
}

impl<EM: EncodedMessage> ReadableMessage for EncodedMessageView<EM> {
    type Code = u8;
    type MessageOption<'a>
        = MessageOption<'a>
    where
        Self: 'a;
    type OptionsIter<'a>
        = OptionsIter<'a>
    where
        Self: 'a;

    fn code(&self) -> u8 {
        self.inner.code()
    }

    // This is one of the not-most-efficient things mentioned in the module introduction: It's
    // iterating through the complete options stream on every payload call, rather than memoizing
    // its location when the options are iterated over.
    fn payload(&self) -> &[u8] {
        let empty: &[u8] = &[];

        // ... into which we'll index
        let optpayload = self.inner.options_and_payload();

        OptPayloadReader::new(optpayload)
            .find(|x| !matches!(x, OptItem::Option { .. }))
            .map_or(empty, |x| {
                if let OptItem::Payload(data) = x {
                    // Can't return data itself because the iterator doesn't outlive this function
                    // To be replaced when ptr_wrapping_offset_from is stabilized
                    let offset = data.as_ptr() as usize - optpayload.as_ptr() as usize;
                    &optpayload[offset..]
                } else {
                    // Silently produce empty payload -- the user will need to have checked the
                    // options and have found the 65535 option.
                    &[]
                }
            })
    }

    fn options(&self) -> OptionsIter<'_> {
        OptionsIter(OptPayloadReader::new(self.inner.options_and_payload()))
    }
}

impl<EM: EncodedMessage> WithSortedOptions for EncodedMessageView<EM> {}