#![doc(html_root_url = "https://docs.rs/kwap-msg/0.6.1")]
#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(not(test), forbid(missing_debug_implementations, unreachable_pub))]
#![cfg_attr(not(test), deny(unsafe_code, missing_copy_implementations))]
#![cfg_attr(any(docsrs, feature = "docs"), feature(doc_cfg))]
#![deny(missing_docs)]
#[cfg(feature = "alloc")]
extern crate alloc as std_alloc;
#[doc(hidden)]
pub mod code;
#[doc(hidden)]
pub mod from_bytes;
#[doc(hidden)]
pub mod opt;
#[doc(hidden)]
pub mod to_bytes;
#[doc(inline)]
pub use code::*;
#[doc(inline)]
pub use from_bytes::{MessageParseError, OptParseError, TryFromBytes};
use kwap_common::{Array, GetSize};
use kwap_macros::rfc_7252_doc;
#[doc(inline)]
pub use opt::*;
#[cfg(feature = "alloc")]
use std_alloc::vec::Vec;
use tinyvec::ArrayVec;
#[doc(inline)]
pub use to_bytes::TryIntoBytes;
#[doc = rfc_7252_doc!("5.5")]
#[derive(Clone, Debug, PartialEq, PartialOrd)]
pub struct Payload<C: Array<Item = u8>>(pub C);
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
pub type VecMessage = Message<Vec<u8>, Vec<u8>, Vec<Opt<Vec<u8>>>>;
pub type ArrayVecMessage<const PAYLOAD_CAP: usize, const N_OPTS: usize, const OPT_CAP: usize> =
Message<ArrayVec<[u8; PAYLOAD_CAP]>,
ArrayVec<[u8; OPT_CAP]>,
ArrayVec<[Opt<ArrayVec<[u8; OPT_CAP]>>; N_OPTS]>>;
#[doc = concat!("\n#", rfc_7252_doc!("2.1"))]
#[doc = concat!("\n#", rfc_7252_doc!("3"))]
#[derive(Clone, PartialEq, PartialOrd, Debug)]
pub struct Message<PayloadC: Array<Item = u8>,
OptC: Array<Item = u8> + 'static,
Opts: Array<Item = Opt<OptC>>> {
pub id: Id,
pub ty: Type,
pub ver: Version,
pub token: Token,
pub code: Code,
pub opts: Opts,
pub payload: Payload<PayloadC>,
}
impl<PayloadC: Array<Item = u8>, OptC: Array<Item = u8> + 'static, Opts: Array<Item = Opt<OptC>>>
Message<PayloadC, OptC, Opts>
{
pub fn ack(&self, id: Id) -> Self {
Self { id,
token: self.token,
ver: Default::default(),
ty: Type::Ack,
code: Code::new(0, 0),
payload: Payload(Default::default()),
opts: Default::default() }
}
}
impl<P: Array<Item = u8>, O: Array<Item = u8>, Os: Array<Item = Opt<O>>> GetSize
for Message<P, O, Os>
{
fn get_size(&self) -> usize {
let header_size = 4;
let payload_marker_size = 1;
let payload_size = self.payload.0.get_size();
let token_size = self.token.0.len();
let opts_size: usize = self.opts.iter().map(|o| o.get_size()).sum();
header_size + payload_marker_size + payload_size + token_size + opts_size
}
fn max_size(&self) -> Option<usize> {
None
}
}
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
pub(crate) struct Byte1 {
pub(crate) ver: Version,
pub(crate) ty: Type,
pub(crate) tkl: u8,
}
#[derive(Copy, Clone, PartialEq, PartialOrd, Debug)]
pub struct Id(pub u16);
#[derive(Copy, Clone, Eq, Ord, PartialEq, PartialOrd, Debug)]
pub enum Type {
Non,
Con,
Ack,
Reset,
}
#[derive(Copy, Clone, PartialEq, PartialOrd, Debug)]
pub struct Version(pub u8);
impl Default for Version {
fn default() -> Self {
Version(1)
}
}
#[doc = rfc_7252_doc!("5.3.1")]
#[derive(Copy, Clone, PartialEq, PartialOrd, Debug)]
pub struct Token(pub tinyvec::ArrayVec<[u8; 8]>);
impl Token {
pub fn opaque(data: &[u8]) -> Token {
use blake2::digest::consts::U8;
use blake2::{Blake2b, Digest};
let mut digest = Blake2b::<U8>::new();
digest.update(data);
Token(Into::<[u8; 8]>::into(digest.finalize()).into())
}
}
#[cfg(test)]
pub(crate) fn test_msg() -> (VecMessage, Vec<u8>) {
let header: [u8; 4] = 0b0100_0001_0100_0101_0000_0000_0000_0001_u32.to_be_bytes();
let token: [u8; 1] = [254u8];
let content_format: &[u8] = b"application/json";
let options: [&[u8]; 2] = [&[0b_1100_1101u8, 0b00000011u8], content_format];
let payload: [&[u8]; 2] = [&[0b1111_1111_u8], b"hello, world!"];
let bytes = [header.as_ref(),
token.as_ref(),
options.concat().as_ref(),
payload.concat().as_ref()].concat();
let mut opts = Vec::new();
let opt = Opt { delta: OptDelta(12),
value: OptValue(content_format.to_vec()) };
opts.push(opt);
let msg = VecMessage { id: Id(1),
ty: Type::Con,
ver: Version(1),
token: Token(tinyvec::array_vec!([u8; 8] => 254)),
opts,
code: Code { class: 2,
detail: 5 },
payload: Payload(b"hello, world!".to_vec()) };
(msg, bytes)
}
#[cfg(test)]
pub(crate) mod tests {
#[macro_export]
macro_rules! assert_eqb {
($actual:expr, $expected:expr) => {
if $actual != $expected {
panic!("expected {:08b} to equal {:08b}", $actual, $expected)
}
};
}
#[macro_export]
macro_rules! assert_eqb_iter {
($actual:expr, $expected:expr) => {
if $actual.iter().ne($expected.iter()) {
panic!("expected {:?} to equal {:?}",
$actual.into_iter()
.map(|b| format!("{:08b}", b))
.collect::<Vec<_>>(),
$expected.into_iter()
.map(|b| format!("{:08b}", b))
.collect::<Vec<_>>())
}
};
}
}