pub mod codes;
pub mod encoded_message;
mod header;
pub mod options;
pub mod token;
use bondrewd::Bitfields;
use heapless::Vec;
use self::{
codes::Code, encoded_message::EncodedMessage, header::MessageHeader, options::CoapOption,
token::Token,
};
pub const PROTOCOL_VERSION: u8 = 1;
#[derive(Debug, Clone, Copy, bondrewd::BitfieldEnum, PartialEq, Eq)]
pub enum Type {
Confirmable = 0,
NonConfirmable = 1,
Acknowledgement = 2,
Reset = 3,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Error {
InvalidCode,
InvalidOption(options::Error),
TokenLength,
OutOfMemory,
MsgTooShort,
EmptyNon,
RequestAck,
NonEmptyRst,
EmptyWithContent,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Message<'data, const MAX_OPTION_COUNT: usize = { crate::DEFAULT_MAX_OPTION_COUNT }> {
version: u8,
message_type: Type,
code: Code,
message_id: u16,
token: Token,
options: Vec<CoapOption<'data>, MAX_OPTION_COUNT>,
payload: Option<&'data [u8]>,
}
impl<'data, const MAX_OPTION_COUNT: usize> TryFrom<EncodedMessage<'data>>
for Message<'data, MAX_OPTION_COUNT>
{
type Error = Error;
fn try_from(encoded: EncodedMessage<'data>) -> Result<Self, Error> {
let mut options: Vec<CoapOption<'_>, MAX_OPTION_COUNT> = Vec::new();
for option in encoded.options_iter()? {
options.push(option.map_err(Error::InvalidOption)?).unwrap();
}
Ok(Self {
version: encoded.version(),
message_type: encoded.message_type(),
code: encoded.code()?,
message_id: encoded.message_id(),
token: encoded.token()?,
options,
payload: encoded.payload()?,
})
}
}
impl<'data, const MAX_OPTION_COUNT: usize> Message<'data, MAX_OPTION_COUNT> {
pub fn new(
message_type: Type,
code: Code,
message_id: u16,
token: Token,
options: Vec<CoapOption<'data>, MAX_OPTION_COUNT>,
payload: Option<&'data [u8]>,
) -> Self {
let options = crate::message::options::sort_options_vec(options);
Self {
version: PROTOCOL_VERSION,
message_type,
code,
message_id,
token,
options,
payload,
}
}
fn new_empty(message_type: Type, message_id: u16) -> Self {
Self {
version: PROTOCOL_VERSION,
message_type,
code: Code::Empty,
message_id,
token: Token::default(),
options: Vec::new(),
payload: None,
}
}
pub fn new_ack(message_id: u16) -> Self {
Self::new_empty(Type::Acknowledgement, message_id)
}
pub fn new_rst(message_id: u16) -> Self {
Self::new_empty(Type::Reset, message_id)
}
pub fn new_ping(message_id: u16) -> Self {
Self::new_empty(Type::Confirmable, message_id)
}
pub fn message_type(&self) -> Type {
self.message_type
}
pub fn code(&self) -> Code {
self.code
}
pub fn message_id(&self) -> u16 {
self.message_id
}
pub fn token(&self) -> Token {
self.token
}
pub fn options(&self) -> &Vec<CoapOption<'_>, MAX_OPTION_COUNT> {
&self.options
}
pub fn payload(&self) -> Option<&'data [u8]> {
self.payload
}
pub fn is_empty(&self) -> bool {
matches!(self.code, Code::Empty)
}
pub fn is_request(&self) -> bool {
matches!(self.code, Code::Request(_))
}
pub fn is_response(&self) -> bool {
matches!(self.code, Code::Response(_))
}
pub(crate) fn encode<'buffer>(
&self,
buf: &'buffer mut [u8],
) -> Result<EncodedMessage<'buffer>, Error> {
let header = MessageHeader::new(
self.version,
self.message_type,
self.token.length,
self.code.into(),
self.message_id,
);
let header = header.into_bytes();
let mut index = 0;
for x in header {
*buf.get_mut(index).ok_or(Error::OutOfMemory)? = x;
index += 1;
}
for i in 0..self.token.length as usize {
*buf.get_mut(index).ok_or(Error::OutOfMemory)? = self.token.bytes[i];
index += 1;
}
index += options::encode_to_buf(&mut buf[index..], &self.options)?;
if let Some(payload) = self.payload {
*buf.get_mut(index).ok_or(Error::OutOfMemory)? = 0xFF; index += 1;
for x in payload {
*buf.get_mut(index).ok_or(Error::OutOfMemory)? = *x;
index += 1;
}
}
let encoded = EncodedMessage::try_new(&buf[..index]).unwrap();
encoded.set_payload_offset(index);
Ok(encoded)
}
}