verdigris 0.1.1

Browser application to explore, learn and debug CoAP
Documentation
//! Tools around coap-message that may once go standalone but are tried out here

use std::convert::TryFrom;

// Could be made to contain a private error detail that is the first critical number, conditional
// on a default feature, and accessible only using an accessor whose presence is conditional on
// that default feature.
#[derive(Debug)]
pub struct CriticalOptionsRemain { }

use coap_numbers::option;
use coap_message::MessageOption;

pub trait OptionsExt<O: MessageOption>: Iterator<Item=O> {
    type IgnoredUriHost: Iterator<Item=O> + Sized;

    fn ignore_uri_host(self) -> Self::IgnoredUriHost;

    fn ignore_elective_others(self) -> Result<(), CriticalOptionsRemain>;
}

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)
    }

    fn ignore_elective_others(self) -> Result<(), CriticalOptionsRemain> {
        match self
            .filter(|o| option::get_criticality(o.number()) == option::Criticality::Critical)
            .next()
        {
            Some(_) => Err(CriticalOptionsRemain { }),
            None => Ok(())
        }
    }
}

/// Call a function (that typically cranks some path state machine) on every (valid) Uri-Path
/// option in an iterator, hiding them from further iteration.
///
/// Error handling of the UTF8 decoding is done by not removing invalid options from the iterator,
/// thus leaving them for an eventual ignore_elective_others.
///
/// FIXME This should become a trait method of OptionsExt, but currently can't be because it'd need
/// to say
///
/// type IgnoreUriHost<F>: impl Iterator<Item=O> + Sized;
/// fn process_uri_path<F>(self, f: F) -> impl Iterator<Item=O>;
///
/// which needs both the type_alias_impl_trait and the generic_associated_types trait. (See the
/// with-gat-and-impl-trait-associated-types branch.)
///
/// FIXME Provide an analogous function that doesn't care about UTF-8. This needs the state
/// machines to run on &[u8], but allows eliding the check (especially as mostly this they will do
/// comparisons or something like atoi).
pub fn process_uri_path<I, O, F>(iter: I, mut f: F) -> impl Iterator<Item=O>
where
    O: MessageOption,
    I: Iterator<Item=O>,
    F: FnMut(&str),
{
    iter.filter(move |o| {
        if o.number() == option::URI_PATH {
            if let Ok(s) = core::str::from_utf8(o.value()) {
                f(s);
                return false;
            }
        }
        true
    })
}


use coap_message::MinimalWritableMessage;
use coap_message::{OptionNumber, FromOtherOption};

// Sure we could make this all type based with apply_single_option(self, ...) -> Self::Tail?
// It's "just" that the type would need a const nextoption, and the zipper construction would need
// to const evaluate them to not have exploding tail versions.

/// A generator of ordered options
///
/// After construction, this can be used with [apply_to_message] to sequentially write all its
/// options into a message (with [apply_to_message_limited] stopping the process halfway).
///
/// Implementors need to provide the [apply_single_option] and [next_option] functions. If a
/// user of the trait prefers to use them over the abovementioned convenience functions, be sure to
/// only call `apply_single_option` when indicated by `next_option`.
///
/// Specialization options are still being explored; ideally, that'd work without introducing any
/// additional orthogonal function names (apply_{to_message{,_limit},single_option}_shortcut or
/// that like), and even better the result type in that case should be `Result<(), !>`.
pub trait OptionFeed<O: OptionNumber> {
    /// Append all the options in the feed to the message.
    ///
    /// This is the main application of OptionFeed.
    ///
    /// It leaves the feed drained.
    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(())
    }

    /// Append options to in the feed to the message up to and including a maximum option number.
    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(())
    }

    /// Append any number of options to the message from this feed as long as their number is as
    /// advertised in [next_option]
    ///
    /// ### Panics
    ///
    /// This may panic if next_option() already indicated that there is no next option in this
    /// feed.
    fn apply_single_option<M: MinimalWritableMessage>(&mut self, message: &mut M) -> Result<(), <M::OptionNumber as FromOtherOption<O>>::Error>;

    /// Option number the feed would write next to the message.
    ///
    /// Returning None indicates that no options are added to the message.
    ///
    /// Implementors may be pessimistic here and give a number even if they then don't actually
    /// produce an option on that number.
    fn next_option(&self) -> Option<u16>;
}

use core::marker::PhantomData;

// The PhantomData here is not strictly essential I think, but should make it easier to get clearer
// error messages when the conflict is between the builder and the new option, and not between the
// builder's previous content and the new option
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;

/// The [OptionFeed] implementation that represents the empty set of options
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
    }
}

/// A single opaque option
// This is a candidate for adding `, const N: u16` as soon as min_const_generics are stable
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())
    }
}


/// A single numeric option
///
/// As the underlying message may have a more efficient add_option_uint, this stores the number in
/// Rust representation and only serializes it with the message.
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),
        }
    }
}