use crate::helpers::Block2RequestData;
use coap_message::MessageOption;
use coap_numbers::option;
#[derive(Debug)]
pub struct BadOption {
unprocessed_option: u16,
}
impl crate::helpers::Renderable for BadOption {
fn render(&self, message: &mut impl coap_message::MinimalWritableMessage) {
let special_code = match self.unprocessed_option {
coap_numbers::option::ACCEPT => Some(coap_numbers::code::NOT_ACCEPTABLE),
coap_numbers::option::PROXY_URI | coap_numbers::option::PROXY_SCHEME => {
Some(coap_numbers::code::PROXYING_NOT_SUPPORTED)
}
coap_numbers::option::CONTENT_FORMAT => {
Some(coap_numbers::code::UNSUPPORTED_CONTENT_FORMAT)
}
_ => None,
};
if let Some(code) = special_code {
message.set_code(crate::helpers::codeconvert(code));
message.set_payload(b"");
return;
}
message.set_code(crate::helpers::codeconvert(coap_numbers::code::BAD_OPTION));
if let Some(n) = coap_numbers::content_format::from_str("application/cbor") {
message.add_option_uint(
crate::helpers::optconvert(coap_numbers::option::CONTENT_FORMAT),
n,
);
}
let mut buf = [161, 39, 25, 0, 0];
buf[3..5].copy_from_slice(&self.unprocessed_option.to_be_bytes());
message.set_payload(&buf);
}
}
pub trait OptionsExt<O: MessageOption>: Iterator<Item = O> {
type IgnoredUriHost: Iterator<Item = O> + Sized;
fn ignore_uri_host(self) -> Self::IgnoredUriHost;
type IgnoredUriQuery: Iterator<Item = O> + Sized;
fn ignore_uri_query(self) -> Self::IgnoredUriQuery;
fn ignore_elective_others(self) -> Result<(), BadOption>;
type WithoutBlock2<'a>: Iterator<Item = O>
where
Self: 'a;
fn take_block2<'a>(self, out: &'a mut Option<Block2RequestData>) -> Self::WithoutBlock2<'a>
where
Self: 'a;
type WithoutUriPath<F: FnMut(&str)>: Iterator<Item = O>;
fn take_uri_path<F: FnMut(&str)>(self, f: F) -> Self::WithoutUriPath<F>;
}
#[cfg(not(feature = "nontrivial_option_processing"))]
mod aux {
use super::*;
pub struct BlockPusher<'a, O: MessageOption, I: Iterator<Item = O>> {
pub(super) instream: I,
pub(super) out: &'a mut Option<Block2RequestData>,
}
impl<'a, O: MessageOption, I: Iterator<Item = O>> Iterator for BlockPusher<'a, O, I> {
type Item = O;
fn next(&mut self) -> Option<O> {
while let Some(o) = self.instream.next() {
if matches!(o.number(), option::BLOCK2) && self.out.is_none() {
if let Ok(o) = Block2RequestData::from_option(&o) {
*self.out = Some(o);
continue;
}
}
return Some(o);
}
None
}
}
pub struct UriPusher<O: MessageOption, I: Iterator<Item = O>, F: FnMut(&str)> {
pub(super) instream: I,
pub(super) f: F,
}
impl<O: MessageOption, I: Iterator<Item = O>, F: FnMut(&str)> Iterator for UriPusher<O, I, F> {
type Item = O;
fn next(&mut self) -> Option<O> {
while let Some(o) = self.instream.next() {
if o.number() == option::URI_PATH {
if let Ok(s) = core::str::from_utf8(o.value()) {
(self.f)(s);
continue;
}
}
return Some(o);
}
None
}
}
}
impl<T, O> OptionsExt<O> for T
where
T: Iterator<Item = O>,
O: MessageOption,
{
type IgnoredUriHost = core::iter::Filter<Self, fn(&O) -> bool>;
fn ignore_uri_host(self) -> Self::IgnoredUriHost {
fn keep_option<O: MessageOption>(o: &O) -> bool {
o.number() != option::URI_HOST
}
self.filter(keep_option)
}
type IgnoredUriQuery = core::iter::Filter<Self, fn(&O) -> bool>;
fn ignore_uri_query(self) -> Self::IgnoredUriQuery {
fn keep_option<O: MessageOption>(o: &O) -> bool {
o.number() != option::URI_QUERY
}
self.filter(keep_option)
}
fn ignore_elective_others(mut self) -> Result<(), BadOption> {
match self.find(|o| option::get_criticality(o.number()) == option::Criticality::Critical) {
Some(o) => Err(BadOption {
unprocessed_option: o.number(),
}),
None => Ok(()),
}
}
#[cfg(not(feature = "nontrivial_option_processing"))]
type WithoutBlock2<'a> = aux::BlockPusher<'a, O, Self> where T: 'a;
#[cfg(not(feature = "nontrivial_option_processing"))]
fn take_block2<'a>(self, out: &'a mut Option<Block2RequestData>) -> Self::WithoutBlock2<'a>
where
T: 'a,
{
aux::BlockPusher {
instream: self,
out,
}
}
#[cfg(feature = "nontrivial_option_processing")]
type WithoutBlock2<'a> = impl Iterator<Item = O> + 'a where Self: 'a;
#[cfg(feature = "nontrivial_option_processing")]
fn take_block2<'a>(self, out: &'a mut Option<Block2RequestData>) -> Self::WithoutBlock2<'a>
where
Self: 'a,
{
self.filter(move |o| {
if matches!(o.number(), option::BLOCK2) && out.is_none() {
if let Ok(o) = Block2RequestData::from_option(o) {
*out = Some(o);
return false;
}
}
true
})
}
#[cfg(not(feature = "nontrivial_option_processing"))]
type WithoutUriPath<F: FnMut(&str)> = aux::UriPusher<O, Self, F>;
#[cfg(not(feature = "nontrivial_option_processing"))]
fn take_uri_path<F: FnMut(&str)>(self, f: F) -> Self::WithoutUriPath<F> {
aux::UriPusher { instream: self, f }
}
#[cfg(feature = "nontrivial_option_processing")]
type WithoutUriPath<F: FnMut(&str)> = impl Iterator<Item = O>;
#[cfg(feature = "nontrivial_option_processing")]
fn take_uri_path<F: FnMut(&str)>(self, mut f: F) -> Self::WithoutUriPath<F> {
self.filter(move |o| {
if o.number() == option::URI_PATH {
if let Ok(s) = core::str::from_utf8(o.value()) {
f(s);
return false;
}
}
true
})
}
}