kwap/
option.rs

1use kwap_common::Array;
2use kwap_msg::{Opt, OptDelta, OptNumber};
3
4/// Content formats supported by kwap
5#[non_exhaustive]
6#[derive(Debug, Clone, Copy)]
7pub enum ContentFormat {
8  /// `text/plain; charset=utf-8`
9  Text,
10  /// `application/link-format`
11  LinkFormat,
12  /// `application/xml`
13  Xml,
14  /// `application/octet-stream`
15  OctetStream,
16  /// `application/exi`
17  Exi,
18  /// `application/json`
19  Json,
20  /// Another content format
21  Other(u16),
22}
23
24impl ContentFormat {
25  /// Convert this content format to the CoAP byte value
26  pub fn bytes(&self) -> [u8; 2] {
27    u16::from(self).to_be_bytes()
28  }
29}
30
31impl<'a> From<&'a ContentFormat> for u16 {
32  fn from(f: &'a ContentFormat) -> Self {
33    use ContentFormat::*;
34    match *f {
35      | Text => 0,
36      | LinkFormat => 40,
37      | Xml => 41,
38      | OctetStream => 42,
39      | Exi => 47,
40      | Json => 50,
41      | Other(n) => n,
42    }
43  }
44}
45
46impl ToCoapValue for ContentFormat {
47  fn to_coap_value<T: Array<Item = u8>>(self) -> T {
48    self.bytes().into_iter().collect()
49  }
50}
51
52/// Something that can be stored in a CoAP Option.
53///
54/// These include:
55/// - strings (str and String)
56/// - empty (`()`)
57/// - unsigned integers (`u8`, `u16`, `u32`, `u64`)
58/// - bytes (anything that impls [`kwap_common::Array`])
59pub trait ToCoapValue {
60  /// Convert the value
61  fn to_coap_value<T: Array<Item = u8>>(self) -> T;
62}
63
64impl<'a> ToCoapValue for &'a str {
65  fn to_coap_value<T: Array<Item = u8>>(self) -> T {
66    self.bytes().collect()
67  }
68}
69
70impl ToCoapValue for core::str::Bytes<'_> {
71  fn to_coap_value<T: Array<Item = u8>>(self) -> T {
72    self.collect()
73  }
74}
75
76impl<A: tinyvec::Array<Item = u8>> ToCoapValue for tinyvec::ArrayVec<A> {
77  fn to_coap_value<T: Array<Item = u8>>(self) -> T {
78    self.into_iter().collect()
79  }
80}
81
82impl ToCoapValue for &[u8] {
83  fn to_coap_value<T: Array<Item = u8>>(self) -> T {
84    self.iter().copied().collect()
85  }
86}
87
88impl ToCoapValue for u8 {
89  fn to_coap_value<T: Array<Item = u8>>(self) -> T {
90    [self].into_iter().collect()
91  }
92}
93
94impl ToCoapValue for u16 {
95  fn to_coap_value<T: Array<Item = u8>>(self) -> T {
96    self.to_be_bytes().into_iter().collect()
97  }
98}
99
100impl ToCoapValue for u32 {
101  fn to_coap_value<T: Array<Item = u8>>(self) -> T {
102    self.to_be_bytes().into_iter().collect()
103  }
104}
105
106impl ToCoapValue for u64 {
107  fn to_coap_value<T: Array<Item = u8>>(self) -> T {
108    self.to_be_bytes().into_iter().collect()
109  }
110}
111
112macro_rules! builder_method {
113  (
114    #[doc = $doc:expr]
115    #[option(num = $nr:literal)]
116    fn $name:ident<$cfg:ty>(string);
117  ) => {
118    #[doc = $doc]
119    pub fn $name<S: AsRef<str>>(self, value: S) -> Self {
120      self.option($nr, value.as_ref())
121    }
122  };
123  (
124    #[doc = $doc:expr]
125    #[option(num = $nr:literal)]
126    fn $name:ident<$cfg:ty>(());
127  ) => {
128    #[doc = $doc]
129    pub fn $name<S: AsRef<str>>(self) -> Self {
130      self.option($nr, &*<$cfg>::MessageOptionBytes::default())
131    }
132  };
133  (
134    #[doc = $doc:expr]
135    #[option(repeatable, num = $nr:literal)]
136    fn $name:ident<$cfg:ty>(string);
137  ) => {
138    #[doc = $doc]
139    pub fn $name<S: AsRef<str>>(self, value: S) -> Self {
140      self.add_option($nr, value.as_ref())
141    }
142  };
143  (
144    #[doc = $doc:expr]
145    #[option(num = $nr:literal)]
146    fn $name:ident<$cfg:ty>($t:ty);
147  ) => {
148    #[doc = $doc]
149    pub fn $name(self, value: $t) -> Self {
150      self.option($nr, value)
151    }
152  };
153  (
154    #[doc = $doc:expr]
155    #[option(repeatable, num = $nr:literal)]
156    fn $name:ident<$cfg:ty>($t:ty);
157  ) => {
158    #[doc = $doc]
159    pub fn $name(self, value: $t) -> Self {
160      self.add_option($nr, value)
161    }
162  };
163}
164
165macro_rules! common_options {
166  ($cfg:ty) => {
167    crate::option::builder_method! {
168      #[doc = kwap_macros::rfc_7252_doc!("5.10.1")]
169      #[option(num = 3)]
170      fn host<$cfg>(string);
171    }
172    crate::option::builder_method! {
173      #[doc = "see [`Self.host()`](#method.host)"]
174      #[option(num = 11)]
175      fn path<$cfg>(string);
176    }
177    crate::option::builder_method! {
178      #[doc = "see [`Self.host()`](#method.host)"]
179      #[option(num = 7)]
180      fn port<$cfg>(u16);
181    }
182    crate::option::builder_method! {
183      #[doc = "see [`Self.host()`](#method.host)"]
184      #[option(repeatable, num = 15)]
185      fn add_query<$cfg>(string);
186    }
187    crate::option::builder_method! {
188      #[doc = kwap_macros::rfc_7252_doc!("5.10.9")]
189      #[option(num = 60)]
190      fn size1<$cfg>(u32);
191    }
192    crate::option::builder_method! {
193      #[doc = kwap_macros::rfc_7252_doc!("5.10.8.1")]
194      #[option(repeatable, num = 1)]
195      fn if_match<$cfg>(tinyvec::ArrayVec<[u8; 8]>);
196    }
197    crate::option::builder_method! {
198      #[doc = kwap_macros::rfc_7252_doc!("5.10.8.2")]
199      #[option(num = 5)]
200      fn if_none_match<$cfg>(());
201    }
202    crate::option::builder_method! {
203      #[doc = kwap_macros::rfc_7252_doc!("5.10.2")]
204      #[option(num = 35)]
205      fn proxy_uri<$cfg>(string);
206    }
207    crate::option::builder_method! {
208      #[doc = "See docs for [`Self.proxy_uri()`](#method.proxy_uri)"]
209      #[option(num = 39)]
210      fn proxy_scheme<$cfg>(string);
211    }
212    crate::option::builder_method! {
213      #[doc = kwap_macros::rfc_7252_doc!("5.10.5")]
214      #[option(num = 14)]
215      fn max_age<$cfg>(u32);
216    }
217    crate::option::builder_method! {
218      #[doc = "See docs for [`Self.location_path()`](#method.location_path)"]
219      #[option(repeatable, num = 20)]
220      fn location_query<$cfg>(string);
221    }
222    crate::option::builder_method! {
223      #[doc = kwap_macros::rfc_7252_doc!("5.10.7")]
224      #[option(repeatable, num = 8)]
225      fn location_path<$cfg>(string);
226    }
227    crate::option::builder_method! {
228      #[doc = concat!(
229                kwap_macros::rfc_7252_doc!("5.10.6"),
230                "\n<details><summary>ETag as a Request Option</summary>\n\n",
231                kwap_macros::rfc_7252_doc!("5.10.6.2"),
232                "\n</details><details><summary>ETag as a Response Option</summary>\n\n",
233                kwap_macros::rfc_7252_doc!("5.10.6.1"),
234                "</details>"
235      )]
236      #[option(repeatable, num = 4)]
237      fn etag<$cfg>(tinyvec::ArrayVec<[u8; 8]>);
238    }
239    crate::option::builder_method! {
240      #[doc = kwap_macros::rfc_7252_doc!("5.10.3")]
241      #[option(num = 12)]
242      fn content_format<$cfg>(crate::ContentFormat);
243    }
244    crate::option::builder_method! {
245      #[doc = kwap_macros::rfc_7252_doc!("5.10.4")]
246      #[option(num = 17)]
247      fn accept<$cfg>(crate::ContentFormat);
248    }
249  };
250}
251
252pub(crate) use {builder_method, common_options};
253
254pub(crate) fn add<A: Array<Item = (OptNumber, Opt<B>)>,
255                  B: Array<Item = u8>,
256                  V: IntoIterator<Item = u8>>(
257  opts: &mut A,
258  repeatable: bool,
259  number: u32,
260  value: V)
261  -> Option<(u32, V)> {
262  use kwap_msg::*;
263
264  let exist = opts.iter_mut().find(|(OptNumber(num), _)| *num == number);
265
266  if !repeatable {
267    if let Some((_, opt)) = exist {
268      opt.value = OptValue(value.into_iter().collect());
269      return None;
270    }
271  }
272
273  let n_opts = opts.get_size() + 1;
274  let no_room = opts.max_size().map(|max| max < n_opts).unwrap_or(false);
275
276  if no_room {
277    return Some((number, value));
278  }
279
280  let num = OptNumber(number);
281  let opt = Opt::<_> { delta: Default::default(),
282                       value: OptValue(value.into_iter().collect()) };
283
284  opts.extend(Some((num, opt)));
285
286  None
287}
288pub(crate) fn normalize<OptNumbers: Array<Item = (OptNumber, Opt<Bytes>)>,
289                        Opts: Array<Item = Opt<Bytes>>,
290                        Bytes: Array<Item = u8>>(
291  mut os: OptNumbers)
292  -> Opts {
293  if os.is_empty() {
294    return Opts::default();
295  }
296
297  os.sort_by_key(|&(OptNumber(num), _)| num);
298  os.into_iter()
299    .fold(Opts::default(), |mut opts, (num, mut opt)| {
300      let delta = opts.iter().fold(0u16, |n, opt| opt.delta.0 + n);
301      opt.delta = OptDelta((num.0 as u16) - delta);
302      opts.push(opt);
303      opts
304    })
305}
306
307#[cfg(test)]
308mod test {
309  use kwap_msg::OptValue;
310
311  use super::*;
312
313  #[test]
314  fn add_updates_when_exist() {
315    let mut opts = vec![(OptNumber(0),
316                         Opt::<Vec<u8>> { delta: OptDelta(0),
317                                          value: OptValue(vec![]) })];
318
319    let out = add(&mut opts, false, 0, vec![1]);
320
321    assert!(out.is_none());
322    assert_eq!(opts.len(), 1);
323    assert_eq!(opts[0].1.value.0, vec![1]);
324  }
325
326  #[test]
327  fn add_adds_when_not_exist() {
328    let mut opts = Vec::<(_, Opt<Vec<u8>>)>::new();
329
330    let out = add(&mut opts, false, 0, vec![1]);
331
332    assert!(out.is_none());
333    assert_eq!(opts.len(), 1);
334    assert_eq!(opts[0].1.value.0, vec![1]);
335  }
336
337  #[test]
338  fn add_adds_when_repeatable() {
339    let mut opts = vec![(OptNumber(0),
340                         Opt::<Vec<u8>> { delta: OptDelta(0),
341                                          value: OptValue(vec![]) })];
342
343    let out = add(&mut opts, true, 0, vec![]);
344
345    assert!(out.is_none());
346    assert_eq!(opts.len(), 2);
347    assert_eq!(opts[0], opts[1]);
348  }
349
350  #[test]
351  fn normalize_opts_echoes_when_empty() {
352    let opts = Vec::<(OptNumber, Opt<Vec<u8>>)>::new();
353    let out = normalize::<_, Vec<Opt<Vec<u8>>>, _>(opts);
354    assert!(out.is_empty())
355  }
356
357  #[test]
358  fn normalize_opts_works() {
359    let opts: Vec<(OptNumber, Opt<Vec<u8>>)> = vec![(OptNumber(32), Default::default()),
360                                                    (OptNumber(1), Default::default()),
361                                                    (OptNumber(3), Default::default()),];
362
363    let expect: Vec<Opt<Vec<u8>>> = vec![Opt { delta: OptDelta(1),
364                                               ..Default::default() },
365                                         Opt { delta: OptDelta(2),
366                                               ..Default::default() },
367                                         Opt { delta: OptDelta(29),
368                                               ..Default::default() },];
369
370    let actual = normalize::<_, Vec<Opt<Vec<u8>>>, _>(opts);
371
372    assert_eq!(actual, expect)
373  }
374
375  #[test]
376  fn add_rets_some_when_full() {
377    let mut opts =
378      tinyvec::ArrayVec::<[(OptNumber, Opt<Vec<u8>>); 1]>::from([(OptNumber(1),
379                                                                  Opt::<Vec<u8>>::default())]);
380
381    let out = add(&mut opts, false, 0, vec![1]);
382
383    assert_eq!(out, Some((0, vec![1])));
384  }
385}