use coap_message::MinimalWritableMessage;
use coap_message::{OptionNumber, FromOtherOption};
pub trait OptionFeed<O: OptionNumber> {
fn apply_to_message<M: MinimalWritableMessage>(&mut self, message: &mut M) -> Result<(), <<M as MinimalWritableMessage>::OptionNumber as TryFrom<u16>>::Error> {
while self.next_option().is_some() {
self.apply_single_option(message)?;
}
Ok(())
}
fn apply_to_message_limited<M: MinimalWritableMessage>(&mut self, message: &mut M, maxopt: u16) -> Result<(), <<M as MinimalWritableMessage>::OptionNumber as TryFrom<u16>>::Error> {
while self.next_option().map(|next| next <= maxopt) == Some(true) {
self.apply_single_option(message)?;
}
Ok(())
}
fn apply_single_option<M: MinimalWritableMessage>(&mut self, message: &mut M) -> Result<(), <M::OptionNumber as FromOtherOption<O>>::Error>;
fn next_option(&self) -> Option<u16>;
}
use core::marker::PhantomData;
pub struct OptionFeedBuilder<O: OptionNumber, F: OptionFeed<O>>(F, PhantomData<O>);
impl<O: OptionNumber> OptionFeedBuilder<O, NoOptions> {
pub fn new() -> Self {
OptionFeedBuilder(NoOptions, PhantomData)
}
}
impl<O: OptionNumber + Clone, F: OptionFeed<O>> OptionFeedBuilder<O, F> {
pub fn add_option<D: AsRef<[u8]>>(self, number: O, value: D) -> OptionFeedBuilder<O, impl OptionFeed<O>> {
OptionFeedBuilder(Zipper(self.0, OpaqueOption::new(number, value), PhantomData), PhantomData)
}
pub fn add_option_uint<Ux: coap_message::numtraits::Ux>(self, number: O, value: Ux) -> OptionFeedBuilder<O, impl OptionFeed<O>> {
OptionFeedBuilder(Zipper(self.0, UintOption::new(number, value), PhantomData), PhantomData)
}
pub fn finish(self) -> F {
self.0
}
}
pub struct NoOptions;
impl<O: OptionNumber> OptionFeed<O> for NoOptions {
fn apply_single_option<M: MinimalWritableMessage>(&mut self, _message: &mut M) -> Result<(), <M::OptionNumber as FromOtherOption<O>>::Error> {
panic!("Called despite None as next_option")
}
fn next_option(&self) -> Option<u16> {
None
}
}
struct OpaqueOption<O: OptionNumber + Clone, D: AsRef<[u8]>>(Option<(O, D)>);
impl<O: OptionNumber + Clone, D: AsRef<[u8]>> OpaqueOption<O, D> {
fn new(number: O, value: D) -> Self {
OpaqueOption(Some((number, value)))
}
}
impl<O: OptionNumber + Clone, D: AsRef<[u8]>> OptionFeed<O> for OpaqueOption<O, D> {
fn apply_single_option<M: MinimalWritableMessage>(&mut self, message: &mut M) -> Result<(), <M::OptionNumber as FromOtherOption<O>>::Error> {
let (num, val) = self.0.take().expect("Feed exhausted");
let num = M::OptionNumber::convert_from_other(num)?;
message.add_option(num, val.as_ref());
Ok(())
}
fn next_option(&self) -> Option<u16> {
self.0.as_ref().map(|s| s.0.clone().into())
}
}
struct UintOption<O: OptionNumber + Clone, Ux: coap_message::numtraits::Ux>(Option<(O, Ux)>);
impl<O: OptionNumber + Clone, Ux: coap_message::numtraits::Ux> UintOption<O, Ux> {
fn new(number: O, value: Ux) -> Self {
UintOption(Some((number, value)))
}
}
impl<O: OptionNumber + Clone, Ux: coap_message::numtraits::Ux> OptionFeed<O> for UintOption<O, Ux> {
fn apply_single_option<M: MinimalWritableMessage>(&mut self, message: &mut M) -> Result<(), <M::OptionNumber as FromOtherOption<O>>::Error> {
let (num, val) = self.0.take().expect("Feed exhausted");
let num = M::OptionNumber::convert_from_other(num)?;
message.add_option_uint(num, val);
Ok(())
}
fn next_option(&self) -> Option<u16> {
self.0.as_ref().map(|s| s.0.clone().into())
}
}
pub struct Zipper<O: OptionNumber, F1: OptionFeed<O>, F2: OptionFeed<O>>(F1, F2, PhantomData<O>);
impl<O: OptionNumber, F1: OptionFeed<O>, F2: OptionFeed<O>> OptionFeed<O> for Zipper<O, F1, F2> {
fn apply_single_option<M: MinimalWritableMessage>(&mut self, message: &mut M) -> Result<(), <M::OptionNumber as FromOtherOption<O>>::Error> {
match (self.0.next_option(), self.1.next_option()) {
(None, None) => panic!("Out of options"),
(None, Some(_)) => self.1.apply_single_option(message)?,
(Some(_), None) => self.0.apply_single_option(message)?,
(Some(x), Some(y)) if x == y => {
self.0.apply_single_option(message)?;
self.1.apply_single_option(message)?;
}
(Some(x), Some(y)) if x < y => self.0.apply_single_option(message)?,
(Some(_), Some(_)) => self.1.apply_single_option(message)?,
}
Ok(())
}
fn next_option(&self) -> Option<u16> {
match (self.0.next_option(), self.1.next_option()) {
(None, None) => None,
(None, Some(y)) => Some(y),
(Some(x), None) => Some(x),
(Some(x), Some(y)) if x <= y => Some(x),
(Some(_), Some(y)) => Some(y),
}
}
}