1use std::collections::{HashMap, hash_map};
2
3use bytes::Buf;
4use num_enum::{FromPrimitive, IntoPrimitive};
5
6use crate::coding::*;
7
8use super::Version;
9use super::{FilterType, Location};
10
11const MAX_PARAMS: u64 = 64;
12const MAX_KVP_VALUE_LEN: usize = (1 << 16) - 1;
14
15#[derive(Debug, Copy, Clone, FromPrimitive, IntoPrimitive, Eq, Hash, PartialEq)]
18#[repr(u64)]
19pub enum ParameterVarInt {
20 MaxRequestId = 2,
22 MaxAuthTokenCacheSize = 4,
23 #[num_enum(catch_all)]
24 Unknown(u64),
25}
26
27#[derive(Debug, Copy, Clone, FromPrimitive, IntoPrimitive, Eq, Hash, PartialEq)]
28#[repr(u64)]
29pub enum ParameterBytes {
30 Path = 1,
31 AuthorizationToken = 3,
32 Authority = 5,
33 Implementation = 7,
34 #[num_enum(catch_all)]
35 Unknown(u64),
36}
37
38#[derive(Default, Debug, Clone)]
39pub struct Parameters {
40 vars: HashMap<ParameterVarInt, u64>,
41 bytes: HashMap<ParameterBytes, Vec<u8>>,
42}
43
44impl Decode<Version> for Parameters {
45 fn decode<R: bytes::Buf>(mut r: &mut R, version: Version) -> Result<Self, DecodeError> {
46 let mut vars = HashMap::new();
47 let mut bytes = HashMap::new();
48
49 match version {
50 Version::Draft17 => {
51 let mut prev_type: u64 = 0;
54 let mut i = 0u64;
55 while r.has_remaining() {
56 if i >= MAX_PARAMS {
57 return Err(DecodeError::TooMany);
58 }
59 let delta = u64::decode(&mut r, version)?;
60 let abs = if i == 0 {
61 delta
62 } else {
63 prev_type.checked_add(delta).ok_or(DecodeError::BoundsExceeded)?
64 };
65 prev_type = abs;
66 i += 1;
67
68 if abs % 2 == 0 {
69 let kind = ParameterVarInt::from(abs);
70 match vars.entry(kind) {
71 hash_map::Entry::Occupied(_) => return Err(DecodeError::Duplicate),
72 hash_map::Entry::Vacant(entry) => entry.insert(u64::decode(&mut r, version)?),
73 };
74 } else {
75 let kind = ParameterBytes::from(abs);
76 let val = Vec::<u8>::decode(&mut r, version)?;
77 if val.len() > MAX_KVP_VALUE_LEN {
78 return Err(DecodeError::BoundsExceeded);
79 }
80 match bytes.entry(kind) {
81 hash_map::Entry::Occupied(_) => return Err(DecodeError::Duplicate),
82 hash_map::Entry::Vacant(entry) => entry.insert(val),
83 };
84 }
85 }
86 }
87 _ => {
88 let count = u64::decode(r, version)?;
89
90 if count > MAX_PARAMS {
91 return Err(DecodeError::TooMany);
92 }
93
94 let mut prev_type: u64 = 0;
95
96 for i in 0..count {
97 let kind = match version {
98 Version::Draft16 => {
99 let delta = u64::decode(r, version)?;
100 let abs = if i == 0 {
101 delta
102 } else {
103 prev_type.checked_add(delta).ok_or(DecodeError::BoundsExceeded)?
104 };
105 prev_type = abs;
106 abs
107 }
108 Version::Draft14 | Version::Draft15 => u64::decode(r, version)?,
109 Version::Draft17 => unreachable!("handled above"),
110 };
111
112 if kind % 2 == 0 {
113 let kind = ParameterVarInt::from(kind);
114 match vars.entry(kind) {
115 hash_map::Entry::Occupied(_) => return Err(DecodeError::Duplicate),
116 hash_map::Entry::Vacant(entry) => entry.insert(u64::decode(&mut r, version)?),
117 };
118 } else {
119 let kind = ParameterBytes::from(kind);
120 let val = Vec::<u8>::decode(&mut r, version)?;
121 if val.len() > MAX_KVP_VALUE_LEN {
122 return Err(DecodeError::BoundsExceeded);
123 }
124 match bytes.entry(kind) {
125 hash_map::Entry::Occupied(_) => return Err(DecodeError::Duplicate),
126 hash_map::Entry::Vacant(entry) => entry.insert(val),
127 };
128 }
129 }
130 }
131 }
132
133 Ok(Parameters { vars, bytes })
134 }
135}
136
137impl Encode<Version> for Parameters {
138 fn encode<W: bytes::BufMut>(&self, w: &mut W, version: Version) -> Result<(), EncodeError> {
139 let count = self.vars.len() + self.bytes.len();
140 if count as u64 > MAX_PARAMS {
141 return Err(EncodeError::TooMany);
142 }
143
144 match version {
145 Version::Draft16 | Version::Draft17 => {
146 if version != Version::Draft17 {
149 count.encode(w, version)?;
150 }
151
152 enum ParamRef<'a> {
154 Var(&'a u64),
155 Bytes(&'a Vec<u8>),
156 }
157 let mut all: Vec<(u64, ParamRef)> = Vec::new();
158 for (k, v) in self.vars.iter() {
159 all.push((u64::from(*k), ParamRef::Var(v)));
160 }
161 for (k, v) in self.bytes.iter() {
162 all.push((u64::from(*k), ParamRef::Bytes(v)));
163 }
164 all.sort_by_key(|(k, _)| *k);
165
166 let mut prev_type: u64 = 0;
167 for (idx, (kind, val)) in all.iter().enumerate() {
168 let delta = if idx == 0 { *kind } else { kind - prev_type };
169 prev_type = *kind;
170 delta.encode(w, version)?;
171
172 match val {
173 ParamRef::Var(v) => v.encode(w, version)?,
174 ParamRef::Bytes(v) => {
175 if v.len() > MAX_KVP_VALUE_LEN {
176 return Err(EncodeError::BoundsExceeded);
177 }
178 v.encode(w, version)?;
179 }
180 }
181 }
182 }
183 Version::Draft14 | Version::Draft15 => {
184 count.encode(w, version)?;
185
186 for (kind, value) in self.vars.iter() {
187 u64::from(*kind).encode(w, version)?;
188 value.encode(w, version)?;
189 }
190
191 for (kind, value) in self.bytes.iter() {
192 if value.len() > MAX_KVP_VALUE_LEN {
193 return Err(EncodeError::BoundsExceeded);
194 }
195 u64::from(*kind).encode(w, version)?;
196 value.encode(w, version)?;
197 }
198 }
199 }
200
201 Ok(())
202 }
203}
204
205impl Parameters {
206 pub fn get_varint(&self, kind: ParameterVarInt) -> Option<u64> {
207 self.vars.get(&kind).copied()
208 }
209
210 pub fn set_varint(&mut self, kind: ParameterVarInt, value: u64) {
211 self.vars.insert(kind, value);
212 }
213
214 #[cfg(test)]
215 pub fn get_bytes(&self, kind: ParameterBytes) -> Option<&[u8]> {
216 self.bytes.get(&kind).map(|v| v.as_slice())
217 }
218
219 pub fn set_bytes(&mut self, kind: ParameterBytes, value: Vec<u8>) {
220 self.bytes.insert(kind, value);
221 }
222}
223
224pub trait Param: Sized {
234 fn param_encode<W: bytes::BufMut>(&self, w: &mut W, version: Version) -> Result<(), EncodeError>;
235 fn param_decode<R: bytes::Buf>(r: &mut R, version: Version) -> Result<Self, DecodeError>;
236
237 fn param_present(&self) -> bool {
239 true
240 }
241}
242
243impl Param for u8 {
244 fn param_encode<W: bytes::BufMut>(&self, w: &mut W, version: Version) -> Result<(), EncodeError> {
245 match version {
246 Version::Draft14 | Version::Draft15 | Version::Draft16 => (*self as u64).encode(w, version),
248 _ => Encode::encode(self, w, version),
249 }
250 }
251
252 fn param_decode<R: bytes::Buf>(r: &mut R, version: Version) -> Result<Self, DecodeError> {
253 match version {
254 Version::Draft14 | Version::Draft15 | Version::Draft16 => {
255 let v = u64::decode(r, version)?;
256 u8::try_from(v).map_err(|_| DecodeError::InvalidValue)
257 }
258 _ => u8::decode(r, version),
259 }
260 }
261}
262
263impl Param for bool {
264 fn param_encode<W: bytes::BufMut>(&self, w: &mut W, version: Version) -> Result<(), EncodeError> {
265 match version {
266 Version::Draft14 | Version::Draft15 | Version::Draft16 => (*self as u64).encode(w, version),
268 _ => Encode::encode(self, w, version),
269 }
270 }
271
272 fn param_decode<R: bytes::Buf>(r: &mut R, version: Version) -> Result<Self, DecodeError> {
273 match version {
274 Version::Draft14 | Version::Draft15 | Version::Draft16 => {
275 let v = u64::decode(r, version)?;
276 match v {
277 0 => Ok(false),
278 1 => Ok(true),
279 _ => Err(DecodeError::InvalidValue),
280 }
281 }
282 _ => bool::decode(r, version),
283 }
284 }
285}
286
287impl Param for u64 {
288 fn param_encode<W: bytes::BufMut>(&self, w: &mut W, version: Version) -> Result<(), EncodeError> {
289 self.encode(w, version)
290 }
291
292 fn param_decode<R: bytes::Buf>(r: &mut R, version: Version) -> Result<Self, DecodeError> {
293 u64::decode(r, version)
294 }
295}
296
297impl Param for Location {
298 fn param_encode<W: bytes::BufMut>(&self, w: &mut W, version: Version) -> Result<(), EncodeError> {
299 match version {
300 Version::Draft14 | Version::Draft15 | Version::Draft16 => {
301 let mut buf = Vec::new();
303 self.group.encode(&mut buf, Version::Draft15)?;
304 self.object.encode(&mut buf, Version::Draft15)?;
305 buf.encode(w, version)?;
306 Ok(())
307 }
308 _ => {
309 self.group.encode(w, version)?;
310 self.object.encode(w, version)?;
311 Ok(())
312 }
313 }
314 }
315
316 fn param_decode<R: bytes::Buf>(r: &mut R, version: Version) -> Result<Self, DecodeError> {
317 match version {
318 Version::Draft14 | Version::Draft15 | Version::Draft16 => {
319 let data = Vec::<u8>::decode(r, version)?;
321 let mut buf = bytes::Bytes::from(data);
322 let group = u64::decode(&mut buf, Version::Draft15)?;
323 let object = u64::decode(&mut buf, Version::Draft15)?;
324 if buf.has_remaining() {
325 return Err(DecodeError::TrailingBytes);
326 }
327 Ok(Location { group, object })
328 }
329 _ => {
330 let group = u64::decode(r, version)?;
331 let object = u64::decode(r, version)?;
332 Ok(Location { group, object })
333 }
334 }
335 }
336}
337
338impl Param for FilterType {
339 fn param_encode<W: bytes::BufMut>(&self, w: &mut W, version: Version) -> Result<(), EncodeError> {
340 let mut buf = Vec::new();
341 let sv = match version {
344 Version::Draft14 | Version::Draft15 | Version::Draft16 => Version::Draft15,
345 _ => version,
346 };
347 self.encode(&mut buf, sv)?;
348 buf.encode(w, version)?;
349 Ok(())
350 }
351
352 fn param_decode<R: bytes::Buf>(r: &mut R, version: Version) -> Result<Self, DecodeError> {
353 let data = Vec::<u8>::decode(r, version)?;
354 let mut buf = bytes::Bytes::from(data);
355 let sv = match version {
356 Version::Draft14 | Version::Draft15 | Version::Draft16 => Version::Draft15,
357 _ => version,
358 };
359 let filter = FilterType::decode(&mut buf, sv)?;
360 if buf.has_remaining() {
361 return Err(DecodeError::TrailingBytes);
362 }
363 Ok(filter)
364 }
365}
366
367impl<T: Param> Param for Option<T> {
368 fn param_present(&self) -> bool {
369 self.is_some()
370 }
371
372 fn param_encode<W: bytes::BufMut>(&self, w: &mut W, version: Version) -> Result<(), EncodeError> {
373 match self {
374 Some(v) => v.param_encode(w, version),
375 None => Ok(()),
376 }
377 }
378
379 fn param_decode<R: bytes::Buf>(r: &mut R, version: Version) -> Result<Self, DecodeError> {
380 Ok(Some(T::param_decode(r, version)?))
381 }
382}
383
384#[macro_export]
396macro_rules! encode_params {
397 ($w:expr, $version:expr, $($key:expr => $val:expr),* $(,)?) => {{
398 #[allow(unused_imports)]
399 use $crate::coding::Encode as _;
400
401 #[allow(unused)]
402 const _: () = {
403 let _keys: &[u64] = &[$($key),*];
404 let mut _i = 1;
405 while _i < _keys.len() {
406 assert!(_keys[_i - 1] < _keys[_i], "parameter keys must be in ascending order");
407 _i += 1;
408 }
409 };
410
411 let _version: $crate::ietf::Version = $version;
412
413 #[allow(unused_mut)]
414 let mut _count: usize = 0;
415 $(_count += if $crate::ietf::Param::param_present(&$val) { 1 } else { 0 };)*
416 _count.encode($w, _version)?;
417
418 #[allow(unused_mut, unused_assignments)]
419 let mut _prev_key: u64 = 0;
420 #[allow(unused_mut, unused_assignments)]
421 let mut _first: bool = true;
422 $(
423 if $crate::ietf::Param::param_present(&$val) {
424 let _key: u64 = $key;
425 match _version {
426 $crate::ietf::Version::Draft14 | $crate::ietf::Version::Draft15 => {
427 _key.encode($w, _version)?;
428 }
429 _ => {
430 let _delta = if _first { _key } else { _key - _prev_key };
431 _delta.encode($w, _version)?;
432 }
433 }
434 _prev_key = _key;
435 _first = false;
436 $crate::ietf::Param::param_encode(&$val, $w, _version)?;
437 }
438 )*
439 }};
440}
441
442#[macro_export]
460macro_rules! decode_params {
461 ($r:expr, $version:expr, $($key:expr => $name:ident: $ty:ty),* $(,)?) => {
462 #[allow(unused)]
463 const _: () = {
464 let _keys: &[u64] = &[$($key),*];
465 let mut _i = 1;
466 while _i < _keys.len() {
467 assert!(_keys[_i - 1] < _keys[_i], "parameter keys must be in ascending order");
468 _i += 1;
469 }
470 };
471
472 $(#[allow(unused_mut, non_snake_case)] let mut $name: Option<$ty> = None;)*
474
475 {
476 #[allow(unused_imports)]
477 use $crate::coding::Decode as _;
478
479 let _version: $crate::ietf::Version = $version;
480 let _count = <u64 as $crate::coding::Decode<$crate::ietf::Version>>::decode($r, _version)?;
481 if _count > 64 {
482 return Err($crate::coding::DecodeError::TooMany);
483 }
484
485 #[allow(unused_mut, unused_assignments)]
486 let mut _prev_key: u64 = 0;
487 for _i in 0.._count {
488 let _key: u64 = match _version {
489 $crate::ietf::Version::Draft14 | $crate::ietf::Version::Draft15 => {
490 <u64 as $crate::coding::Decode<$crate::ietf::Version>>::decode($r, _version)?
491 }
492 _ => {
493 let _delta = <u64 as $crate::coding::Decode<$crate::ietf::Version>>::decode($r, _version)?;
494 let _abs = if _i == 0 {
495 _delta
496 } else {
497 _prev_key.checked_add(_delta).ok_or($crate::coding::DecodeError::BoundsExceeded)?
498 };
499 _prev_key = _abs;
500 _abs
501 }
502 };
503
504 match _key {
505 $($key => {
506 if $name.is_some() {
507 return Err($crate::coding::DecodeError::Duplicate);
508 }
509 $name = Some(<$ty as $crate::ietf::Param>::param_decode($r, _version)?);
510 })*
511 _ => return Err($crate::coding::DecodeError::InvalidValue),
512 }
513 }
514 }
515
516 $(#[allow(unused_variables)] let $name: $ty = $name.unwrap_or_default();)*
518 };
519}
520
521#[cfg(test)]
522mod tests {
523 use super::*;
524 use bytes::{Buf, BytesMut};
525
526 #[test]
529 fn test_parameters_v16_delta_round_trip() {
530 let mut params = Parameters::default();
531 params.set_bytes(ParameterBytes::Path, b"/test".to_vec());
532 params.set_varint(ParameterVarInt::MaxRequestId, 100);
533 params.set_bytes(ParameterBytes::Implementation, b"test-impl".to_vec());
534
535 let mut buf = BytesMut::new();
536 params.encode(&mut buf, Version::Draft16).unwrap();
537
538 let mut bytes = buf.freeze();
539 let decoded = Parameters::decode(&mut bytes, Version::Draft16).unwrap();
540
541 assert_eq!(decoded.get_bytes(ParameterBytes::Path), Some(b"/test".as_ref()));
542 assert_eq!(decoded.get_varint(ParameterVarInt::MaxRequestId), Some(100));
543 assert_eq!(
544 decoded.get_bytes(ParameterBytes::Implementation),
545 Some(b"test-impl".as_ref())
546 );
547 }
548
549 #[test]
550 fn test_parameters_v15_round_trip() {
551 let mut params = Parameters::default();
552 params.set_bytes(ParameterBytes::Path, b"/test".to_vec());
553 params.set_varint(ParameterVarInt::MaxRequestId, 100);
554
555 let mut buf = BytesMut::new();
556 params.encode(&mut buf, Version::Draft15).unwrap();
557
558 let mut bytes = buf.freeze();
559 let decoded = Parameters::decode(&mut bytes, Version::Draft15).unwrap();
560
561 assert_eq!(decoded.get_bytes(ParameterBytes::Path), Some(b"/test".as_ref()));
562 assert_eq!(decoded.get_varint(ParameterVarInt::MaxRequestId), Some(100));
563 }
564
565 #[test]
566 fn test_parameters_v17_round_trip() {
567 let mut params = Parameters::default();
568 params.set_bytes(ParameterBytes::Path, b"/test".to_vec());
569 params.set_varint(ParameterVarInt::MaxAuthTokenCacheSize, 4096);
570 params.set_bytes(ParameterBytes::Implementation, b"test-impl".to_vec());
571
572 let mut buf = BytesMut::new();
573 params.encode(&mut buf, Version::Draft17).unwrap();
574
575 let mut bytes = buf.freeze();
576 let decoded = Parameters::decode(&mut bytes, Version::Draft17).unwrap();
577
578 assert_eq!(decoded.get_bytes(ParameterBytes::Path), Some(b"/test".as_ref()));
579 assert_eq!(decoded.get_varint(ParameterVarInt::MaxAuthTokenCacheSize), Some(4096));
580 assert_eq!(
581 decoded.get_bytes(ParameterBytes::Implementation),
582 Some(b"test-impl".as_ref())
583 );
584 assert!(!bytes.has_remaining());
585 }
586
587 #[test]
588 fn test_parameters_v17_no_count_prefix() {
589 let mut params = Parameters::default();
590 params.set_bytes(ParameterBytes::Path, b"/x".to_vec());
591
592 let mut buf15 = BytesMut::new();
593 params.encode(&mut buf15, Version::Draft15).unwrap();
594
595 let mut buf17 = BytesMut::new();
596 params.encode(&mut buf17, Version::Draft17).unwrap();
597
598 assert!(buf17.len() < buf15.len());
599 }
600
601 fn round_trip_params(
604 version: Version,
605 encode_fn: impl FnOnce(&mut BytesMut, Version) -> Result<(), EncodeError>,
606 decode_fn: impl FnOnce(&mut bytes::Bytes, Version) -> Result<(), DecodeError>,
607 ) {
608 let mut buf = BytesMut::new();
609 encode_fn(&mut buf, version).unwrap();
610 let mut bytes = buf.freeze();
611 decode_fn(&mut bytes, version).unwrap();
612 assert!(!bytes.has_remaining(), "buffer not fully consumed for {version}");
613 }
614
615 #[test]
616 fn test_param_u8_all_versions() {
617 for version in [Version::Draft14, Version::Draft15, Version::Draft16, Version::Draft17] {
618 round_trip_params(
619 version,
620 |w, v| {
621 encode_params!(w, v, 0x20 => 200u8);
622 Ok(())
623 },
624 |r, v| {
625 decode_params!(r, v, 0x20 => val: Option<u8>);
626 assert_eq!(val, Some(200));
627 Ok(())
628 },
629 );
630 }
631 }
632
633 #[test]
634 fn test_param_bool_all_versions() {
635 for version in [Version::Draft14, Version::Draft15, Version::Draft16, Version::Draft17] {
636 round_trip_params(
637 version,
638 |w, v| {
639 encode_params!(w, v, 0x10 => true);
640 Ok(())
641 },
642 |r, v| {
643 decode_params!(r, v, 0x10 => val: Option<bool>);
644 assert_eq!(val, Some(true));
645 Ok(())
646 },
647 );
648 }
649 }
650
651 #[test]
652 fn test_param_location_all_versions() {
653 let loc = Location { group: 5, object: 3 };
654 for version in [Version::Draft14, Version::Draft15, Version::Draft16, Version::Draft17] {
655 round_trip_params(
656 version,
657 |w, v| {
658 encode_params!(w, v, 0x09 => loc.clone());
659 Ok(())
660 },
661 |r, v| {
662 decode_params!(r, v, 0x09 => val: Option<Location>);
663 assert_eq!(val, Some(Location { group: 5, object: 3 }));
664 Ok(())
665 },
666 );
667 }
668 }
669
670 #[test]
671 fn test_param_filter_type_all_versions() {
672 for version in [Version::Draft14, Version::Draft15, Version::Draft16, Version::Draft17] {
673 round_trip_params(
674 version,
675 |w, v| {
676 encode_params!(w, v, 0x21 => FilterType::LargestObject);
677 Ok(())
678 },
679 |r, v| {
680 decode_params!(r, v, 0x21 => val: Option<FilterType>);
681 assert_eq!(val, Some(FilterType::LargestObject));
682 Ok(())
683 },
684 );
685 }
686 }
687
688 #[test]
689 fn test_param_multiple_delta_encoding() {
690 for version in [Version::Draft14, Version::Draft15, Version::Draft16, Version::Draft17] {
691 round_trip_params(
692 version,
693 |w, v| {
694 encode_params!(w, v,
695 0x10 => true,
696 0x20 => 200u8,
697 0x21 => FilterType::LargestObject,
698 0x22 => 2u8,
699 );
700 Ok(())
701 },
702 |r, v| {
703 decode_params!(r, v,
704 0x10 => forward: Option<bool>,
705 0x20 => sub_pri: Option<u8>,
706 0x21 => filter: Option<FilterType>,
707 0x22 => group_order: Option<u8>,
708 );
709 assert_eq!(forward, Some(true));
710 assert_eq!(sub_pri, Some(200));
711 assert_eq!(filter, Some(FilterType::LargestObject));
712 assert_eq!(group_order, Some(2));
713 Ok(())
714 },
715 );
716 }
717 }
718
719 #[test]
720 fn test_param_empty_set() {
721 for version in [Version::Draft14, Version::Draft15, Version::Draft16, Version::Draft17] {
722 round_trip_params(
723 version,
724 |w, v| {
725 encode_params!(w, v,);
726 Ok(())
727 },
728 |r, v| {
729 decode_params!(r, v,);
730 Ok(())
731 },
732 );
733 }
734 }
735
736 #[test]
737 fn test_param_option_skip_none() {
738 for version in [Version::Draft14, Version::Draft15, Version::Draft16, Version::Draft17] {
739 round_trip_params(
740 version,
741 |w, v| {
742 let loc: Option<Location> = None;
743 encode_params!(w, v,
744 0x09 => loc,
745 0x10 => true,
746 );
747 Ok(())
748 },
749 |r, v| {
750 decode_params!(r, v,
751 0x09 => loc: Option<Location>,
752 0x10 => forward: Option<bool>,
753 );
754 assert_eq!(loc, None);
755 assert_eq!(forward, Some(true));
756 Ok(())
757 },
758 );
759 }
760 }
761
762 #[test]
763 fn test_param_option_encode_some() {
764 for version in [Version::Draft14, Version::Draft15, Version::Draft16, Version::Draft17] {
765 round_trip_params(
766 version,
767 |w, v| {
768 let loc = Some(Location { group: 10, object: 5 });
769 encode_params!(w, v,
770 0x09 => loc,
771 0x10 => true,
772 );
773 Ok(())
774 },
775 |r, v| {
776 decode_params!(r, v,
777 0x09 => loc: Option<Location>,
778 0x10 => forward: Option<bool>,
779 );
780 assert_eq!(loc, Some(Location { group: 10, object: 5 }));
781 assert_eq!(forward, Some(true));
782 Ok(())
783 },
784 );
785 }
786 }
787
788 #[test]
789 fn test_param_bare_type_defaults() {
790 for version in [Version::Draft14, Version::Draft15, Version::Draft16, Version::Draft17] {
792 round_trip_params(
793 version,
794 |w, v| {
795 encode_params!(w, v, 0x10 => true);
797 Ok(())
798 },
799 |r, v| {
800 decode_params!(r, v,
801 0x10 => forward: bool,
802 0x20 => priority: u8,
803 );
804 assert!(forward);
805 assert_eq!(priority, 0); Ok(())
807 },
808 );
809 }
810 }
811
812 #[test]
813 fn test_param_unknown_rejected() {
814 for version in [Version::Draft14, Version::Draft15, Version::Draft16, Version::Draft17] {
816 let mut buf = BytesMut::new();
817 1usize.encode(&mut buf, version).unwrap();
818 0x10u64.encode(&mut buf, version).unwrap();
819 true.param_encode(&mut buf, version).unwrap();
820
821 let mut bytes = buf.freeze();
822 let result: Result<(), DecodeError> = (|| {
823 decode_params!(&mut bytes, version, 0x20 => val: Option<u8>);
824 let _ = val;
825 Ok(())
826 })();
827 assert!(
828 matches!(result, Err(DecodeError::InvalidValue)),
829 "expected InvalidValue for unknown param in {version}"
830 );
831 }
832 }
833
834 #[test]
835 fn test_param_duplicate_rejected() {
836 for version in [Version::Draft14, Version::Draft15, Version::Draft16, Version::Draft17] {
838 let mut buf = BytesMut::new();
839 2usize.encode(&mut buf, version).unwrap();
841 match version {
842 Version::Draft16 | Version::Draft17 => {
843 0x20u64.encode(&mut buf, version).unwrap();
845 100u8.param_encode(&mut buf, version).unwrap();
846 0u64.encode(&mut buf, version).unwrap();
848 200u8.param_encode(&mut buf, version).unwrap();
849 }
850 _ => {
851 0x20u64.encode(&mut buf, version).unwrap();
853 100u8.param_encode(&mut buf, version).unwrap();
854 0x20u64.encode(&mut buf, version).unwrap();
856 200u8.param_encode(&mut buf, version).unwrap();
857 }
858 }
859
860 let mut bytes = buf.freeze();
861 let result: Result<(), DecodeError> = (|| {
862 decode_params!(&mut bytes, version, 0x20 => val: Option<u8>);
863 let _ = val;
864 Ok(())
865 })();
866 assert!(
867 matches!(result, Err(DecodeError::Duplicate)),
868 "expected Duplicate for {version}"
869 );
870 }
871 }
872}