coap_message_utils/
option_processing.rs

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