verdigris 0.2.0

Browser application to explore, learn and debug CoAP
/** A writable CoAP message in CoAP-over-WebSocket serialization
 *
 * Writes are append-only, as this stores the message in a Vec<u8> with no intention to reorder
 * fields; for random-access construction consider building a HeapCoAPMessage first and then
 * populating from there.
 *
 * FIXME: This currently does no error checking whatsoever, and will build invalid messages if
 * driven wrong.
 */
#[derive(Debug, Clone)]
pub struct CoAPWSMessageW {
    data: Vec<u8>,
    option_base: u16,
}

use coap_message_utils::option_extension::encode_extensions;

impl CoAPWSMessageW {
    pub fn new(token: &[u8]) -> Self {
        assert!(token.len() <= 8);
        let mut data = Vec::with_capacity(token.len() + 2);
        data.push(token.len() as u8);
        data.push(0); // Initially empty, needs to be set with set_code
        data.extend_from_slice(&token);

        Self { data, option_base: 0 }
    }

    pub fn serialize(self) -> Vec<u8> {
        self.data
    }
}

impl coap_message::MinimalWritableMessage for CoAPWSMessageW {
    type Code = u8;
    type OptionNumber = u16;

    fn set_code(&mut self, code: u8) {
        self.data[1] = code;
    }

    fn add_option(&mut self, optnum: u16, value: &[u8]) {
        assert!(value.len() <= u16::MAX.into());
        let delta = optnum - self.option_base;
        self.option_base = optnum;
        let len = value.len() as u16;

        self.data.extend_from_slice(encode_extensions(delta, len).as_ref());
        self.data.extend_from_slice(value);
    }

    fn set_payload(&mut self, payload: &[u8]) {
        if payload.len() > 0 {
            self.data.push(0xff);
            self.data.extend_from_slice(payload);
        }
    }
}

use coap_message_utils::inmemory::{EncodedMessageView, EncodedMessage};

/** A CoAP message in CoAP-over-WebSocket serialization
 *
 * The object is checked for validity of the header fields (length field, token length), but not
 * for validity of options or payload.
 * */
#[derive(Debug, Clone)]
pub struct CoAPWSMessageR<B: AsRef<[u8]>>(EncodedMessageView<CoAPWSEncodedMessage<B>>);

#[derive(Clone, Debug)]
// It's not like we're handing out this type, but its associated types are taken to be
// CoAPWSMessageR's, and thus need to be public.
pub struct CoAPWSEncodedMessage<B: AsRef<[u8]>>(B);

impl<B: AsRef<[u8]>> EncodedMessage for CoAPWSEncodedMessage<B> {
    fn code(&self) -> u8 {
        self.0.as_ref()[1]
    }

    fn options_and_payload(&self) -> &[u8] {
        &self.0.as_ref()[(self.0.as_ref()[0] as usize) + 2..]
    }
}

impl<B: AsRef<[u8]>> CoAPWSMessageR<B> {
    pub fn new(data: B) -> Result<Self, &'static str> {
        let len_tkl = *data.as_ref().get(0).ok_or("Zero message")?;
        if len_tkl > 8 {
            return Err("Invalid first byte");
        }
        let tkl = len_tkl;
        if data.as_ref().len() < 2 + (tkl as usize) {
            return Err("Message too short for token");
        }

        Ok(Self(EncodedMessageView::new(CoAPWSEncodedMessage(data))))
    }

    pub fn token(&self) -> &[u8] {
        let original_slice = self.0.as_ref().0.as_ref();
        &original_slice[2..2 + (original_slice[0] as usize)]
    }
}

/// Implementation delegating fully to the wrapped EncodedMessageView
impl<B: AsRef<[u8]>> coap_message::ReadableMessage for CoAPWSMessageR<B> {
    type Code = <EncodedMessageView<CoAPWSEncodedMessage<B>> as coap_message::ReadableMessage>::Code;
    type MessageOption<'a> = <EncodedMessageView<CoAPWSEncodedMessage<B>> as coap_message::ReadableMessage>::MessageOption<'a> where Self: 'a;
    type OptionsIter<'a> = <EncodedMessageView<CoAPWSEncodedMessage<B>> as coap_message::ReadableMessage>::OptionsIter<'a> where Self: 'a;

    fn code(&self) -> u8 {
        self.0.code()
    }
    fn payload(&self) -> &[u8] {
        self.0.payload()
    }
    fn options(&self) -> Self::OptionsIter<'_> {
        self.0.options()
    }
}