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