1use kwap_common::Array;
2use kwap_msg::{Opt, OptDelta, OptNumber};
3
4#[non_exhaustive]
6#[derive(Debug, Clone, Copy)]
7pub enum ContentFormat {
8 Text,
10 LinkFormat,
12 Xml,
14 OctetStream,
16 Exi,
18 Json,
20 Other(u16),
22}
23
24impl ContentFormat {
25 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
52pub trait ToCoapValue {
60 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}