use crate::helpers::{block2_write_with_cf, codeconvert, Block2RequestData, Renderable};
use crate::{wkc, Code};
use coap_handler::{Handler, Reporting};
use coap_message::{MessageOption, MutableWritableMessage, ReadableMessage};
use coap_numbers::{code, option};
pub struct SimpleRenderableData(Result<Block2RequestData, Code>);
pub trait SimpleRenderable {
fn render<W: embedded_io::blocking::Write + core::fmt::Write>(&mut self, writer: &mut W);
fn content_format(&self) -> Option<u16> {
None
}
}
#[derive(Debug, Copy, Clone)]
pub struct SimpleRendered<T: SimpleRenderable>(pub T);
impl<'a> SimpleRendered<TypedStaticRenderable<'a>> {
pub fn new_typed_slice(data: &'a [u8], content_format: Option<u16>) -> Self {
SimpleRendered(TypedStaticRenderable {
data,
content_format,
})
}
pub fn new_typed_str(data: &'a str, content_format: Option<u16>) -> Self {
let data = data.as_bytes();
Self::new_typed_slice(data, content_format)
}
}
impl<T> Handler for SimpleRendered<T>
where
T: SimpleRenderable,
{
type RequestData = SimpleRenderableData;
fn extract_request_data(&mut self, request: &impl ReadableMessage) -> Self::RequestData {
let expected_accept = self.0.content_format();
let mut block2 = Ok(None);
for o in request.options() {
match o.number() {
coap_numbers::option::ACCEPT => {
if expected_accept.is_some() && o.value_uint() != expected_accept {
return SimpleRenderableData(Err(Code(coap_numbers::code::NOT_ACCEPTABLE)));
}
}
coap_numbers::option::BLOCK2 => {
block2 = match block2 {
Err(e) => Err(e),
Ok(Some(_)) => Err(Code(coap_numbers::code::BAD_REQUEST)),
Ok(None) => Block2RequestData::from_option(&o)
.map(Some)
.map_err(|_| Code(code::BAD_REQUEST)),
}
}
o if option::get_criticality(o) == option::Criticality::Critical => {
return SimpleRenderableData(Err(Code(code::BAD_OPTION)));
}
_ => (),
}
}
let reqdata = match request.code().into() {
code::GET => block2.map(|o| o.unwrap_or_default()),
_ => Err(Code(code::METHOD_NOT_ALLOWED)),
};
SimpleRenderableData(reqdata)
}
fn estimate_length(&mut self, _request: &Self::RequestData) -> usize {
1280 - 40 - 4 }
fn build_response(
&mut self,
response: &mut impl MutableWritableMessage,
request: Self::RequestData,
) {
let block2data = match request.0 {
Ok(x) => x,
Err(c) => {
c.render(response);
return;
}
};
let cf = self.0.content_format();
response.set_code(codeconvert(code::CONTENT));
block2_write_with_cf(block2data, response, |w| self.0.render(w), cf);
}
}
impl<T> Reporting for SimpleRendered<T>
where
T: SimpleRenderable,
{
type Record<'a> = wkc::EmptyRecord
where
Self: 'a,
;
type Reporter<'a> = core::iter::Once<wkc::EmptyRecord>
where
Self: 'a,
;
fn report(&self) -> Self::Reporter<'_> {
core::iter::once(wkc::EmptyRecord {})
}
}
impl<'a> SimpleRenderable for &'a str {
fn render<W>(&mut self, writer: &mut W)
where
W: core::fmt::Write,
{
writer
.write_str(self)
.expect("The backend of SimpleRenderable supports infallible writing");
}
fn content_format(&self) -> Option<u16> {
coap_numbers::content_format::from_str("text/plain; charset=utf-8")
}
}
pub struct TypedStaticRenderable<'a> {
data: &'a [u8],
content_format: Option<u16>,
}
impl<'a> SimpleRenderable for TypedStaticRenderable<'a> {
fn render<W: embedded_io::blocking::Write + core::fmt::Write>(&mut self, writer: &mut W) {
writer.write_all(self.data).unwrap();
}
fn content_format(&self) -> Option<u16> {
self.content_format
}
}