async_coap/option/
iter.rs

1// Copyright 2019 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14//
15
16use super::*;
17use std::convert::Into;
18
19/// A convenience iterator for parsing options from a byte buffer.
20#[derive(Debug, Clone)]
21pub struct OptionIterator<'a> {
22    iter: core::slice::Iter<'a, u8>,
23    last_option: OptionNumber,
24}
25
26impl<'a> Default for OptionIterator<'a> {
27    fn default() -> Self {
28        OptionIterator::new(&[])
29    }
30}
31
32impl<'a> OptionIterator<'a> {
33    /// Creates a new instance of an `OptionIterator` with the given byte slice.
34    pub fn new(buffer: &'a [u8]) -> OptionIterator<'a> {
35        OptionIterator {
36            iter: buffer.iter(),
37            last_option: Default::default(),
38        }
39    }
40
41    /// Returns the unread remaining options as a byte slice.
42    pub fn as_slice(&self) -> &'a [u8] {
43        self.iter.as_slice()
44    }
45
46    /// Peek ahead to the next option without moving the iterator forward.
47    pub fn peek(&mut self) -> Option<Result<(OptionNumber, &'a [u8]), Error>> {
48        decode_option(&mut self.iter.clone(), self.last_option).transpose()
49    }
50
51    /// Determine if the next option has a specific number and value without moving the
52    /// iterator forward.
53    pub fn peek_eq<T>(&mut self, key: OptionKey<T>, value: T) -> bool
54    where
55        T: Into<OptionValue<'a>>,
56    {
57        let mut temp_array = [0; 8];
58        match decode_option(&mut self.iter.clone(), self.last_option) {
59            Ok(Some((number, iter_value))) => {
60                number == key.0
61                    && (match value.into() {
62                        OptionValue::Integer(x) => encode_u32(x, &mut temp_array),
63                        OptionValue::Bytes(x) => x,
64                        OptionValue::ETag(x) => {
65                            let temp_slice = &mut temp_array[0..x.len()];
66                            temp_slice.copy_from_slice(x.as_bytes());
67                            temp_slice
68                        }
69                    } == iter_value)
70            }
71            _ => false,
72        }
73    }
74}
75
76impl<'a> Iterator for OptionIterator<'a> {
77    type Item = Result<(OptionNumber, &'a [u8]), Error>;
78
79    fn next(&mut self) -> Option<Self::Item> {
80        let ret = decode_option(&mut self.iter, self.last_option).transpose();
81        if let Some(Ok((key, _))) = ret {
82            self.last_option = key;
83        }
84        ret
85    }
86}
87
88impl AsRef<[u8]> for OptionIterator<'_> {
89    fn as_ref(&self) -> &[u8] {
90        self.as_slice()
91    }
92}
93
94/// Extension trait for option iterators that provide additional convenient accessors.
95pub trait OptionIteratorExt<'a>: Iterator<Item = Result<(OptionNumber, &'a [u8]), Error>> {
96    /// Moves the iterator forward until it finds a matching key or the
97    /// spot where it should have been.
98    ///
99    /// If found, returns the option number and a byte slice of the value.
100    ///
101    /// Does not consume any options after
102    /// the matching key.
103    fn find_next(&mut self, key: OptionNumber) -> Option<Result<(OptionNumber, &'a [u8]), Error>>;
104
105    /// Typed version of [`OptionIteratorExt::find_next`].
106    ///
107    /// Moves the iterator forward until it finds a matching key or the
108    /// spot where it should have been.
109    ///
110    /// If found, returns the value of the option key.
111    ///
112    /// Does not consume any options after
113    /// the matching key.
114    fn find_next_of<T>(&mut self, key: OptionKey<T>) -> Option<Result<T, Error>>
115    where
116        T: TryOptionValueFrom<'a> + Sized,
117    {
118        if let Some(result) = self.find_next(key.0) {
119            match result {
120                Ok((_, value)) => {
121                    if let Some(x) = T::try_option_value_from(value) {
122                        return Some(Ok(x));
123                    } else {
124                        return Some(Err(Error::ParseFailure));
125                    }
126                }
127                Err(e) => return Some(Err(e)),
128            }
129        }
130
131        None
132    }
133
134    /// Extracts a URI relative-reference from the remaining URI_PATH and URI_QUERY options,
135    /// moving the iterator past them.
136    fn extract_uri(&self) -> Result<RelRefBuf, Error>
137    where
138        Self: Sized + Clone,
139    {
140        let mut copy = self.clone();
141        let mut buf = String::new();
142
143        while let Some(seg) = copy.find_next_of(option::URI_PATH).transpose()? {
144            if !buf.is_empty() {
145                buf.push('/');
146            }
147            buf.extend(seg.escape_uri());
148        }
149
150        let mut has_query = false;
151
152        while let Some(item) = copy.find_next_of(option::URI_QUERY).transpose()? {
153            if has_query {
154                buf.push('&');
155            } else {
156                buf.push('?');
157                has_query = true;
158            }
159            buf.extend(item.escape_uri().for_query());
160        }
161
162        let mut ret = RelRefBuf::from_string(buf).expect("Constructed URI was malformed");
163
164        ret.disambiguate();
165
166        Ok(ret)
167    }
168
169    /// Extracts a URI relative-reference from the remaining LOCATION_PATH and LOCATION_QUERY options,
170    /// moving the iterator past them.
171    fn extract_location(&self) -> Result<RelRefBuf, Error>
172    where
173        Self: Sized + Clone,
174    {
175        let mut copy = self.clone();
176        let mut buf = String::new();
177
178        while let Some(seg) = copy.find_next_of(option::LOCATION_PATH).transpose()? {
179            if !buf.is_empty() {
180                buf.push('/');
181            }
182            buf.extend(seg.escape_uri());
183        }
184
185        let mut has_query = false;
186
187        while let Some(item) = copy.find_next_of(option::LOCATION_QUERY).transpose()? {
188            if has_query {
189                buf.push('&');
190            } else {
191                buf.push('?');
192                has_query = true;
193            }
194            buf.extend(item.escape_uri().for_query());
195        }
196
197        // TODO: Check out those reserved Location-* options and fail if found.
198        //       See <https://tools.ietf.org/html/rfc7252#section-5.10.7> for more info.
199
200        Ok(RelRefBuf::from_string(buf).expect("Constructed URI was malformed"))
201    }
202}
203
204impl<'a, I> OptionIteratorExt<'a> for I
205where
206    I: Iterator<Item = Result<(OptionNumber, &'a [u8]), Error>> + Sized + Clone,
207{
208    fn find_next(&mut self, key: OptionNumber) -> Option<Result<(OptionNumber, &'a [u8]), Error>> {
209        let next_value = loop {
210            let mut iter = self.clone();
211
212            match iter.next()? {
213                Err(x) => return Some(Err(x)),
214                Ok((number, value)) => {
215                    if number == key {
216                        *self = iter;
217                        break (key, value);
218                    }
219                    if number < key.0 {
220                        *self = iter;
221                        continue;
222                    }
223                }
224            };
225
226            return None;
227        };
228
229        Some(Ok(next_value))
230    }
231}