use crate::Error;
use crate::helpers::block2_write_with_cf;
use coap_handler::Handler;
use coap_message::{
Code as _, MessageOption, MinimalWritableMessage, MutableWritableMessage, ReadableMessage,
};
use coap_message_utils::{OptionsExt, option_value::Block2RequestData};
use coap_numbers::{code, option};
use core::marker::PhantomData;
pub(crate) mod fillers;
pub(crate) mod generated;
pub struct Empty;
pub trait TypeRenderable:
GetRenderable
+ PostRenderable
+ PutRenderable
+ DeleteRenderable
+ FetchRenderable
+ IPatchRenderable
{
}
pub trait GetRenderable {
type Get;
fn get(&mut self) -> Result<Self::Get, Error> {
Err(Error::method_not_allowed())
}
}
pub trait PostRenderable {
type PostIn;
type PostOut;
fn post(&mut self, _representation: &Self::PostIn) -> Result<Self::PostOut, Error> {
Err(Error::method_not_allowed())
}
}
pub trait PutRenderable {
type Put;
fn put(&mut self, _representation: &Self::Put) -> Result<(), Error> {
Err(Error::method_not_allowed())
}
}
pub trait DeleteRenderable {
fn delete(&mut self) -> Result<(), Error> {
Err(Error::method_not_allowed())
}
}
pub trait FetchRenderable {
type FetchIn;
type FetchOut;
fn fetch(&mut self, _representation: &Self::FetchIn) -> Result<Self::FetchOut, Error> {
Err(Error::method_not_allowed())
}
}
pub trait IPatchRenderable {
type IPatch;
fn ipatch(&mut self, _representation: &Self::IPatch) -> Result<(), Error> {
Err(Error::method_not_allowed())
}
}
mod sealed {
pub trait TypeSerializer {
const CF: Option<u16>;
}
pub struct MiniCBORSerialization2;
}
use sealed::*;
pub struct TypeHandler<H, S: TypeSerializer>
where
H: TypeRenderable,
{
handler: H,
_phantom: PhantomData<S>,
}
impl<H, S> TypeHandler<H, S>
where
H: TypeRenderable,
S: TypeSerializer,
{
fn extract_options(request: &impl ReadableMessage) -> Result<Option<Block2RequestData>, Error> {
let mut block2 = None;
request
.options()
.take_block2(&mut block2)
.filter(|o| {
if o.number() == option::CONTENT_FORMAT || o.number() == option::ACCEPT {
if let Some(cf) = S::CF {
o.value_uint() != Some(cf)
} else {
true
}
} else {
true
}
})
.ignore_elective_others()?;
Ok(block2)
}
}
pub struct TypeRequestData<FetchIn, PostOut>(TypeRequestDataE<FetchIn, PostOut>);
enum TypeRequestDataE<FetchIn, PostOut> {
Get(Block2RequestData), Fetch(Block2RequestData, FetchIn),
Post(PostOut),
DoneCode(u8), }
use self::TypeRequestDataE::{DoneCode, Fetch, Get, Post};
trait ToTypedResourceError {
fn into_general_error(self, total_len: usize) -> Error;
}
impl<'b, C> minicbor_2::decode::Decode<'b, C> for Empty {
fn decode(
_d: &mut minicbor_2::decode::Decoder<'b>,
_ctx: &mut C,
) -> Result<Self, minicbor_2::decode::Error> {
Err(minicbor_2::decode::Error::message("No element expected").at(0))
}
fn nil() -> Option<Self> {
Some(Empty)
}
}
impl<C> minicbor_2::encode::Encode<C> for Empty {
fn encode<W: minicbor_2::encode::Write>(
&self,
_e: &mut minicbor_2::Encoder<W>,
_ctx: &mut C,
) -> Result<(), minicbor_2::encode::Error<W::Error>> {
Ok(())
}
}
impl<C> minicbor_2::encode::CborLen<C> for Empty {
fn cbor_len(&self, _ctx: &mut C) -> usize {
0
}
}
impl<H> Handler for TypeHandler<H, MiniCBORSerialization2>
where
H: TypeRenderable,
H::Get: minicbor_2::Encode<()>,
H::PostIn: for<'de> minicbor_2::Decode<'de, ()>,
H::PostOut: minicbor_2::Encode<()> + minicbor_2::CborLen<()>,
H::Put: for<'de> minicbor_2::Decode<'de, ()>,
H::FetchIn: for<'de> minicbor_2::Decode<'de, ()>,
H::FetchOut: minicbor_2::Encode<()>,
H::IPatch: for<'de> minicbor_2::Decode<'de, ()>,
{
type RequestData = TypeRequestData<H::FetchIn, H::PostOut>;
type ExtractRequestError = Error;
type BuildResponseError<M: MinimalWritableMessage> = Error;
fn extract_request_data<M: ReadableMessage>(
&mut self,
request: &M,
) -> Result<Self::RequestData, Error> {
let block2 = Self::extract_options(request)?;
if matches!(
request.code().into(),
code::DELETE | code::POST | code::PUT | code::IPATCH
) && block2.is_some()
{
return Err(Error::bad_option(coap_numbers::option::BLOCK2));
}
Ok(TypeRequestData(match request.code().into() {
code::DELETE => {
self.handler.delete()?;
DoneCode(code::DELETED)
}
code::GET => Get(block2.unwrap_or_default()),
code::POST => {
use minicbor_2::decode::Decode;
let payload = request.payload();
match (payload, H::PostIn::nil()) {
(b"", Some(nil)) => Post(self.handler.post(&nil)?),
(payload, _) => {
let parsed: H::PostIn =
minicbor_2::decode(payload).map_err(|deserialize_error| {
deserialize_error.into_general_error(payload.len())
})?;
Post(self.handler.post(&parsed)?)
}
}
}
code::PUT => {
let payload = request.payload();
let parsed: H::Put = minicbor_2::decode(payload).map_err(|deserialize_error| {
deserialize_error.into_general_error(payload.len())
})?;
self.handler.put(&parsed)?;
DoneCode(code::CHANGED)
}
code::FETCH => {
let payload = request.payload();
let parsed: H::FetchIn =
minicbor_2::decode(payload).map_err(|deserialize_error| {
deserialize_error.into_general_error(payload.len())
})?;
Fetch(block2.unwrap_or_default(), parsed)
}
code::IPATCH => {
let payload = request.payload();
let parsed: H::IPatch =
minicbor_2::decode(payload).map_err(|deserialize_error| {
deserialize_error.into_general_error(payload.len())
})?;
self.handler.ipatch(&parsed)?;
DoneCode(code::CHANGED)
}
_ => return Err(Error::method_not_allowed()),
}))
}
fn estimate_length(&mut self, request: &Self::RequestData) -> usize {
match &request.0 {
DoneCode(_) => 4,
Get(block) => (block.size() + 25).into(), _ => 1200,
}
}
fn build_response<M: MutableWritableMessage>(
&mut self,
response: &mut M,
request: Self::RequestData,
) -> Result<(), Error> {
match request.0 {
DoneCode(c) => response.set_code(M::Code::new(c).map_err(Error::from_unionerror)?),
Get(block2) => {
let repr = self.handler.get()?;
response.set_code(M::Code::new(code::CONTENT).map_err(Error::from_unionerror)?);
block2_write_with_cf(
block2,
response,
|win| minicbor_2::encode(&repr, minicbor_adapters::WriteToEmbeddedIo(win)),
MiniCBORSerialization2::CF,
)
.map_err(|_| Error::internal_server_error())?;
}
Fetch(block2, fetch) => {
let repr = self.handler.fetch(&fetch)?;
response.set_code(M::Code::new(code::CONTENT).map_err(Error::from_unionerror)?);
block2_write_with_cf(
block2,
response,
|win| minicbor_2::encode(&repr, minicbor_adapters::WriteToEmbeddedIo(win)),
MiniCBORSerialization2::CF,
)
.map_err(|_| Error::internal_server_error())?;
}
Post(post_out) => {
response.set_code(M::Code::new(code::CHANGED).map_err(Error::from_unionerror)?);
let len = minicbor_2::len(&post_out);
let payload = response
.payload_mut_with_len(len)
.map_err(|_| Error::internal_server_error())?;
let mut encoder = minicbor_2::Encoder::new(payload);
encoder
.encode(&post_out)
.map_err(|_| Error::internal_server_error())?;
}
};
Ok(())
}
}
impl TypeSerializer for MiniCBORSerialization2 {
const CF: Option<u16> = coap_numbers::content_format::from_str("application/cbor");
}
impl<H> TypeHandler<H, MiniCBORSerialization2>
where
H: TypeRenderable,
H::Get: minicbor_2::Encode<()>,
H::PostIn: for<'de> minicbor_2::Decode<'de, ()>,
H::PostOut: minicbor_2::Encode<()>,
H::Put: for<'de> minicbor_2::Decode<'de, ()>,
H::FetchIn: for<'de> minicbor_2::Decode<'de, ()>,
H::FetchOut: minicbor_2::Encode<()>,
H::IPatch: for<'de> minicbor_2::Decode<'de, ()>,
{
pub fn new_minicbor_2(handler: H) -> Self {
TypeHandler {
handler,
_phantom: PhantomData,
}
}
}
impl ToTypedResourceError for minicbor_2::decode::Error {
fn into_general_error(self, total_len: usize) -> Error {
let mut error = Error::bad_request();
if let Some(position) = self.position() {
error = Error::bad_request_with_rbep(position);
}
if self.is_end_of_input() {
error = Error::bad_request_with_rbep(total_len);
};
if self.is_type_mismatch() {
error = error.with_title("Type mismatch")
}
error
}
}