use super::*;
use core::cell::Cell;
impl<'a> Message<SliceBuffer<'a>> {
#[must_use]
pub fn new_from_slice(code: u8, options_and_payload: &'a [u8]) -> Self {
Message::new(SliceBuffer::new(code, options_and_payload))
}
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Message<EM: MessageBuffer> {
inner: EM,
payload_cache: Cell<Option<Processing>>,
end: usize,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub(crate) enum Processing {
OkPayloadLength(usize),
OkNoPayload,
Error,
}
#[cfg(feature = "downcast")]
fn type_id_of_emv_of_lml<T: 'static + MessageBuffer>(
_val: &LifetimesMatterLittle<T>,
) -> core::any::TypeId {
core::any::TypeId::of::<Message<T>>()
}
impl<EM: MessageBuffer> Message<EM> {
pub fn new(inner: EM) -> Self {
Self {
end: inner.tail().len(),
payload_cache: Cell::default(),
inner,
}
}
pub fn new_until(inner: EM, length: usize) -> Self {
Self {
end: length,
payload_cache: Cell::default(),
inner,
}
}
#[cfg(feature = "downcast")]
pub fn downcast_from<M: ReadableMessage>(generic: &M) -> Option<&Self> {
let (reference, type_id) = generic.with_static_type_annotation()?.into_inner();
let our_static = EM::static_variant()?;
let our_id = type_id_of_emv_of_lml(&our_static);
if type_id != our_id {
return None;
}
let ptr = reference as *const M as *const Self;
Some(unsafe { &*ptr })
}
fn ensure_parsed(&self) -> Processing {
if let Some(v) = self.payload_cache.get() {
v
} else {
self.options().for_each(|_| ());
self.payload_cache
.get()
.expect("Side effect of options iteration is that this should have been set")
}
}
pub fn into_mutable(self) -> Result<MessageMut<EM>, ParsingError>
where
EM: MessageBufferMut,
{
let payload_len = match self.ensure_parsed() {
Processing::OkPayloadLength(l) => l,
Processing::OkNoPayload => 0,
Processing::Error => return Err(ParsingError(())),
};
Ok(MessageMut {
encoded: self.inner,
end: self.end,
latest: u16::MAX,
payload_start: match payload_len {
0 => None,
l => Some(self.end - l),
},
})
}
}
#[derive(Debug, thiserror::Error)]
#[error("CoAP message was not well-formed")]
pub struct ParsingError(());
impl<EM: MessageBuffer> AsRef<EM> for Message<EM> {
fn as_ref(&self) -> &EM {
&self.inner
}
}
impl<EM: MessageBuffer> ReadableMessage for Message<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 len = match self.ensure_parsed() {
Processing::OkPayloadLength(l) => l,
Processing::OkNoPayload => 0,
Processing::Error => 0,
};
let optpayload = &self.inner.tail()[..self.end];
&optpayload[optpayload.len() - len..]
}
fn options(&self) -> OptionsIter<'_> {
OptionsIter(
crate::option_iteration::OptPayloadReader::new(&self.inner.tail()[..self.end]),
Some(&self.payload_cache),
)
}
#[cfg(feature = "downcast")]
fn with_static_type_annotation(
&self,
) -> Option<coap_message::helpers::RefWithStaticType<'_, Self>> {
let em_static = EM::static_variant()?;
Some(unsafe {
coap_message::helpers::RefWithStaticType::new(self, type_id_of_emv_of_lml(&em_static))
})
}
}
impl<EM: MessageBuffer> WithSortedOptions for Message<EM> {}