#![cfg_attr(feature = "downcast", allow(unsafe_code))]
use coap_message::{ReadableMessage, WithSortedOptions};
use crate::option_iteration::{OptItem, OptPayloadReader};
pub struct OptionsIter<'a>(pub(crate) OptPayloadReader<'a>);
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: &[],
}),
Some(OptItem::Payload(_)) => None,
None => None,
}
}
}
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
}
}
#[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;
}
let ptr = reference as *const M as *const Self;
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>> {
Some(unsafe {
coap_message::helpers::RefWithStaticType::new(
self,
core::any::TypeId::of::<Message<'static>>(),
)
})
}
}
impl WithSortedOptions for Message<'_> {}
pub trait EncodedMessage {
fn code(&self) -> u8;
fn options_and_payload(&self) -> &[u8];
}
#[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,
}
}
}
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()
}
fn payload(&self) -> &[u8] {
let empty: &[u8] = &[];
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 {
let offset = data.as_ptr() as usize - optpayload.as_ptr() as usize;
&optpayload[offset..]
} else {
&[]
}
})
}
fn options(&self) -> OptionsIter<'_> {
OptionsIter(OptPayloadReader::new(self.inner.options_and_payload()))
}
}
impl<EM: EncodedMessage> WithSortedOptions for EncodedMessageView<EM> {}