coap-message 0.1.0

Interface to CoAP messages
Documentation
/// Iteration item for option values
///
/// An implementation needs to allow the user to get the value as a memory slice. This is trivial
/// for messages that are stored in serialized form. Implementations that store options
/// semantically (eg. as a `struct Block { n: usize, m: bool, szx: u8 }`) will typically make their
/// MessageOption large enough to contain serialized options, or heap-allocate for them.
pub trait MessageOption {
    fn number(&self) -> u16;
    fn value(&self) -> &[u8];

    /// Obtain the option's value as a text string, or None if the option contains invalid UTF-8.
    ///
    /// Implementations can override this to reduce the string checking overhead if they already
    /// have the value as a string internally.
    fn value_str(&self) -> Option<&str> {
        core::str::from_utf8(self.value()).ok()
    }

    /// Obtain the option's value as a number following the `uint` [value
    /// format](https://tools.ietf.org/html/rfc7252#section-3.2), or None if the option is too
    /// long.
    ///
    /// Implementations can override this to reduce conversion overhead if they already have a
    /// numeric value internally as soon as U's type is replaced with an equally capable public num
    /// trait.
    fn value_uint<U: crate::numtraits::Ux>(&self) -> Option<U> {
        let mut bufarray: U::Bytes = Default::default();
        let buf = bufarray.as_mut();
        let buflen = buf.len();
        let val = self.value();
        if val.len() > buflen {
            return None;
        }
        buf[buflen - val.len()..].copy_from_slice(val);
        Some(U::from_be_bytes(bufarray))
    }
}

/// Marker trait that indicates that ReadableMessage::options are produced in ascending
/// sequence.
pub trait WithSortedOptions<'a>: ReadableMessage<'a> {}

pub trait ReadableMessage<'a> {
    type Code: crate::numbers::Code;
    type MessageOption: 'a + MessageOption;
    type OptionsIter: Iterator<Item = Self::MessageOption>;

    fn code(&self) -> Self::Code;

    fn payload(&self) -> &[u8];

    /// Produce all options in arbitrary order as an iterator
    ///
    /// If your options are always produced in an ordered fashion, consider implementing the
    /// ``WithSortedOptions`` trait as well. This should be the case for most CoAP
    /// message backends. Examples of backends where it is not implemented are single-pass reads
    /// over in-place decrypted OSCORE messages.
    fn options(&'a self) -> Self::OptionsIter;
}

// It would be nice to have more type state in here (for headers, last option number and whether
// payload has been set); this is a first step that can easily wrap jnet and maybe gcoap. Taking
// the next step is likely to happen soon, given that jnet coap has already moved to type state.
/// A message that needs to have its code, any options in ascending order and its payload set in
/// that very sequence.
///
/// This is the bare minimum a message needs to provide to be populated as a request or response by
/// a generic program; it is up to the program to ensure the valid sequence of operations, as
/// failure to do so may incur panics (FIXME: or errors).
pub trait MinimalWritableMessage {
    type Code: crate::numbers::Code;
    type OptionNumber: crate::numbers::OptionNumber;

    fn set_code(&mut self, code: Self::Code);

    /// Add an option to the message
    ///
    /// Calls to this method need to happen in ascending numeric sequence.
    ///
    /// This works on option values as they are encoded in messages. Under the aspect of [option
    /// value formats](https://tools.ietf.org/html/rfc7252#section-3.2), this adds opaque options
    /// (but may just as well be used for adding options in another format when they are
    /// pre-encoded).
    // completely ignoring error handling here, pending typestateification
    fn add_option(&mut self, number: Self::OptionNumber, value: &[u8]);

    // error handling as in add_option
    fn set_payload(&mut self, data: &[u8]);

    /// Copy code, options and payload in from a readable message
    ///
    /// Implementations can override this for cases where it can be done more efficiently than
    /// iterating over the options and appending them.
    fn set_from_message<'m, M>(&mut self, msg: &'m M)
    where
        M: ReadableMessage<'m>,
    {
        use core::convert::TryInto;

        self.set_code(
            msg.code()
                .into()
                .try_into()
                .map_err(|_| "Code can not be expressed in target message")
                .unwrap(),
        );

        for opt in msg.options() {
            self.add_option(
                opt.number()
                    .try_into()
                    .map_err(|_| "Option can not be expressed in target message")
                    .unwrap(),
                opt.value(),
            )
        }
        self.set_payload(msg.payload());
    }

    /// Shortcut for `add_option(self, number, value.as_bytes())`.
    ///
    /// Implementations with type checked options can provide more efficient implementations (ie.
    /// ones that don't need to UTF-8-check when they feed the resulting bytes back into a string
    /// field), but must still accept string options via the generic
    /// [`add_option()`](MinimalWritableMessage::add_option)
    /// method.
    fn add_option_str(&mut self, number: Self::OptionNumber, value: &str) {
        self.add_option(number, value.as_bytes())
    }

    /// Shortcut for `add_option` on a buffer containing the uint encoded value
    ///
    /// Implementations with type checked options can provide more efficient implementations (ie.
    /// ones that don't need to decode the uint when reading it into a uint field), but must still
    /// accept integer options via the generic [`add_option()`](MinimalWritableMessage::add_option)
    /// method.
    ///
    /// While the trait under U is hidden (pending the use of a more generic one num-types based
    /// one), own implementations are not possible.
    fn add_option_uint<U: crate::numtraits::Ux>(&mut self, number: Self::OptionNumber, value: U) {
        // This would be much easier with https://github.com/rust-num/num-traits/issues/189 solved
        let value = value.to_be_bytes();
        let mut value = value.as_ref();
        while let Some(&0) = value.get(0) {
            value = &value[1..];
        }
        self.add_option(number, value)
    }
}

/// A message that allows later manipulation of a once set payload, and later truncation.
///
/// This is a bit of an unsorted bag that needs further cleanup (FIXME) -- most of this is
/// motivated by block-wise and write-in-place. Might need a bit of reshape, possibly something
/// like a once-callable ``.write_payload(|d: &mut [u8]| { write_to(d); Ok(bytes_written)})``. Does
/// this need a hint of the length to allocate for implementations that don't pre-allocate the
/// message? Is 1024 a good enough value to not pass it?
///
/// The available_space is only needed where applications want to use up the last byte by not
/// zero-padding the Block2 option to its szx=0 equivalent.
///
/// Can that be efficiently be replaced with something like this, and can it be optimized down to
/// the hand-written counting-of-option-bytes that's involved in the use of available_space?
///
/// ```ignore
/// let mut m = allocated_message;
/// for szx in 6..0 {
///     snap = m.snapshot();
///     m.add_option(BLOCK2, ...);
///     m.add_option(..., ...);
///
///     if let Ok(_) = m.write_payload(|p| {
///         if (p.len() < 1 << (4 + szx)) {
///             return Err(());
///         }
///
///         let written = write_block(...);
///
///         Ok(written)
///     }) {
///         break;
///     } else {
///         m = m.revert_to(snap);
///     }
/// } else {
///     panic!("Allocated space doesn't even suffice for 16 byte payload");
/// }
/// ```
///
pub trait MutableWritableMessage: MinimalWritableMessage {
    /// Number of bytes available for additional options, payload marker and payload
    fn available_space(&self) -> usize;

    // to be reevaluated, currently here for compatibility / easy migration
    fn payload_mut(&mut self) -> &mut [u8];

    fn truncate(&mut self, len: usize);

    fn mutate_options<F>(&mut self, callback: F)
    where
        F: FnMut(Self::OptionNumber, &mut [u8]);
}

/// Marker trait that indicates that the sequence of calling set_code, add_option and set_payload
/// is not fixed. The sequence of calls only has meaning in that later set_code and set_payload
/// calls override earlier ones, and that add_option on the same option number are stored in their
/// sequence.
// FIXME: Look into whether there's any implementation where it'd make sense to only have some of
// the relaxation but not all (eg. all options must be out, then comes the code).
pub trait SeekWritableMessage {
    // FIXME: Provide a more generic set_from_message that does not demand
    // WithSortedOptions. It can even have just the same code.
}