coap_message_utils/option_processing.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
use crate::option_value::{Block2RequestData, TryFromOption};
use crate::Error;
use coap_message::MessageOption;
use coap_numbers::option;
/// Extensions implemented for any [MessageOption] iterator to enable simple and direct use
///
/// As an extension trait, this is not meant to be implemented (in fact, Rust's rules prohibit this
/// from being implemented anywhere else after this crate's `impl<T, O> OptionsExt<O> for T`), but
/// can be .
///
/// This will typically be used by filtering down a message's options, e.g. like this:
///
/// ```
/// # struct ReqData {
/// # block2: coap_message_utils::option_value::Block2RequestData,
/// # }
///
/// use coap_message::{MessageOption, ReadableMessage};
/// use coap_message_utils::{Error, OptionsExt};
///
/// fn process_message(req: &impl ReadableMessage) -> Result<ReqData, Error>
/// {
/// let mut block2 = None;
///
/// req.options()
/// .take_block2(&mut block2)
/// .filter(|o| {
/// match o.number() {
/// // my own option => my own behavior
/// _ => true
/// }
/// })
/// .ignore_elective_others()
/// ?;
///
/// let block2 = block2.unwrap_or_default();
/// Ok(ReqData { block2 })
/// }
/// ```
// No need to seal this: the `for T` implementation already ensures that nobody implements it.
pub trait OptionsExt<O: MessageOption>: Iterator<Item = O> {
/// Remove Uri-Host option from the iterator
///
/// Note that not processing this may be inappropriate for security reasons, especially with
/// security models that otherwise require DNS rebinding protection.
fn ignore_uri_host(self) -> impl Iterator<Item = O>;
/// Remove Uri-Query options from the iterator
///
/// Note that this is *not* something that should simply be placed in a handler; it should only
/// be used if the definition of the resource's interface explicitly allows the implementation
/// to ignore unknown query parameters.
fn ignore_uri_query(self) -> impl Iterator<Item = O>;
/// Exhaust the iterator, successfully if no critical options are present, or indicating an
/// error if critical options were not processed before.
fn ignore_elective_others(self) -> Result<(), Error>;
/// Store the first matching and parsable option into `out`, and return the iterator over the
/// remaining options.
///
/// Unparsable or duplicate options are simply left in the output and rely on their criticality
/// to cause an error when eventually `.ignore_elective_others()` is called. When using this
/// mechanism to parse a non-critical option that should not be ignored on parsing errors, that
/// option should not implement [`TryFromOption`] on `Self`, but rather on `Result<Self,
/// ParsingError>`, and `if Some(Err(_)) = out` after the options have been exhausted, the
/// error needs to be raised.
fn take_into<'a, T: TryFromOption>(self, out: &'a mut Option<T>) -> impl Iterator<Item = O>
where
Self: 'a;
/// Set out to the parsed value of the found Block2 option, and return an iterator over the
/// remaining options.
///
/// Unparsable or repeated Block2 options are left in the output, leaving the error to show up
/// in the eventual ignore_elective_others call.
///
/// Note that this is merely a pre-typed version of take_into (and not deprecated yet because
/// it's a convenient shortcut to spelling out the `None`'s type).
fn take_block2<'a>(self, out: &'a mut Option<Block2RequestData>) -> impl Iterator<Item = O>
where
Self: 'a;
/// 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.
fn take_uri_path<F: FnMut(&str)>(self, f: F) -> impl Iterator<Item = O>;
}
impl<T, O> OptionsExt<O> for T
where
T: Iterator<Item = O>,
O: MessageOption,
{
fn ignore_uri_host(self) -> impl Iterator<Item = O> {
self.filter(|o| o.number() != option::URI_HOST)
}
fn ignore_uri_query(self) -> impl Iterator<Item = O> {
self.filter(|o| o.number() != option::URI_QUERY)
}
fn ignore_elective_others(mut self) -> Result<(), Error> {
match self.find(|o| option::get_criticality(o.number()) == option::Criticality::Critical) {
Some(o) => Err(Error::bad_option(o.number())),
None => Ok(()),
}
}
fn take_into<'a, T2: 'a + TryFromOption>(
self,
out: &'a mut Option<T2>,
) -> impl Iterator<Item = O>
where
T: 'a,
{
self.filter(move |o| {
if out.is_none() {
if let Some(o) = T2::try_from(o) {
*out = Some(o);
return false;
}
}
true
})
}
fn take_block2<'a>(self, out: &'a mut Option<Block2RequestData>) -> impl Iterator<Item = O>
where
Self: 'a,
{
self.take_into(out)
}
fn take_uri_path<F: FnMut(&str)>(self, mut f: F) -> impl Iterator<Item = O> {
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
})
}
}