1use core::ops::{Deref, DerefMut};
6
7use bitfield::{bitfield_bitrange, bitfield_fields};
8use byte::{check_len, BytesExt, TryRead, TryWrite};
9use heapless::String;
10
11pub trait MsgType {
12 const MSG_TYPE: u8;
13}
14
15#[derive(Clone, Copy, Debug, Default, PartialEq)]
16#[cfg_attr(feature = "defmt", derive(defmt::Format))]
17pub struct Flags(u8);
18bitfield_bitrange! {struct Flags(u8)}
19
20impl Flags {
21 bitfield_fields! {
22 u8;
23 pub dup, set_dup: 7;
24 pub qos, set_qos: 6, 5;
25 pub retain, set_retain: 4;
26 pub will, set_will: 3;
27 pub clean_session, set_clean_session: 2;
28 pub topic_id_type, set_topic_id_type: 1, 0;
29 }
30}
31
32impl TryWrite for Flags {
33 fn try_write(self, bytes: &mut [u8], _ctx: ()) -> byte::Result<usize> {
34 let offset = &mut 0;
35 bytes.write(offset, self.0)?;
36 Ok(*offset)
37 }
38}
39
40impl TryRead<'_> for Flags {
41 fn try_read(bytes: &[u8], _ctx: ()) -> byte::Result<(Self, usize)> {
42 Ok((Flags(bytes.read::<u8>(&mut 0)?), 1))
43 }
44}
45
46#[derive(Clone, Copy, Debug, PartialEq)]
47#[cfg_attr(feature = "defmt", derive(defmt::Format))]
48pub enum ReturnCode {
49 Accepted,
50 Rejected(RejectedReason),
51}
52
53impl From<RejectedReason> for ReturnCode {
54 fn from(reason: RejectedReason) -> Self {
55 Self::Rejected(reason)
56 }
57}
58
59impl TryWrite for ReturnCode {
60 fn try_write(self, bytes: &mut [u8], _ctx: ()) -> byte::Result<usize> {
61 let offset = &mut 0;
62 bytes.write(
63 offset,
64 match self {
65 ReturnCode::Accepted => 0u8,
66 ReturnCode::Rejected(RejectedReason::Congestion) => 1u8,
67 ReturnCode::Rejected(RejectedReason::InvalidTopicId) => 2u8,
68 ReturnCode::Rejected(RejectedReason::NotSupported) => 3u8,
69 ReturnCode::Rejected(RejectedReason::Reserved(n)) => n,
70 },
71 )?;
72 Ok(*offset)
73 }
74}
75
76impl TryRead<'_> for ReturnCode {
77 fn try_read(bytes: &[u8], _ctx: ()) -> byte::Result<(Self, usize)> {
78 let offset = &mut 0;
79 Ok((
80 match bytes.read::<u8>(offset)? {
81 0 => ReturnCode::Accepted,
82 1 => ReturnCode::Rejected(RejectedReason::Congestion),
83 2 => ReturnCode::Rejected(RejectedReason::InvalidTopicId),
84 3 => ReturnCode::Rejected(RejectedReason::NotSupported),
85 n => ReturnCode::Rejected(RejectedReason::Reserved(n)),
86 },
87 *offset,
88 ))
89 }
90}
91
92#[derive(Clone, Copy, Debug, PartialEq)]
93#[cfg_attr(feature = "defmt", derive(defmt::Format))]
94pub enum RejectedReason {
95 Congestion,
96 InvalidTopicId,
97 NotSupported,
98 Reserved(u8),
99}
100
101#[derive(Clone, Debug, PartialEq)]
102#[cfg_attr(feature = "defmt", derive(defmt::Format))]
103pub enum MaybeForwardedMessage {
104 ForwardedMessage(ForwardedMessage),
105 Message(Message),
106}
107
108impl From<ForwardedMessage> for MaybeForwardedMessage {
109 fn from(msg: ForwardedMessage) -> Self {
110 Self::ForwardedMessage(msg)
111 }
112}
113
114impl<M: Into<Message>> From<M> for MaybeForwardedMessage {
115 fn from(msg: M) -> Self {
116 Self::Message(msg.into())
117 }
118}
119
120impl TryWrite for MaybeForwardedMessage {
121 fn try_write(self, bytes: &mut [u8], _ctx: ()) -> byte::Result<usize> {
122 let offset = &mut 0;
123 match self {
124 MaybeForwardedMessage::ForwardedMessage(msg) => bytes.write(offset, msg),
125 MaybeForwardedMessage::Message(msg) => bytes.write(offset, msg),
126 }?;
127 Ok(*offset)
128 }
129}
130
131impl TryRead<'_> for MaybeForwardedMessage {
132 fn try_read(bytes: &[u8], _ctx: ()) -> byte::Result<(Self, usize)> {
133 let offset = &mut 0;
134 check_len(bytes, 2)?;
135 let msg_type: u8 = bytes.read(&mut 1usize)?;
136 if msg_type == 0xfe {
137 let fw_msg: ForwardedMessage = bytes.read(offset)?;
138 Ok((fw_msg.into(), *offset))
139 } else {
140 let msg: Message = bytes.read(offset)?;
141 Ok((msg.into(), *offset))
142 }
143 }
144}
145
146#[derive(Clone, Debug, PartialEq)]
147#[cfg_attr(feature = "defmt", derive(defmt::Format))]
148pub struct ForwardedMessage {
149 pub ctrl: u8,
150 pub wireless_node_id: WirelessNodeId,
151 pub message: Message,
152}
153
154impl TryWrite for ForwardedMessage {
155 fn try_write(self, bytes: &mut [u8], _ctx: ()) -> byte::Result<usize> {
156 let offset = &mut 0;
157 bytes.write(offset, 3 + self.wireless_node_id.len() as u8)?; bytes.write(offset, 0xFEu8)?; bytes.write(offset, self.ctrl)?;
160 bytes.write(offset, self.wireless_node_id.as_str())?;
161 bytes.write(offset, self.message)?;
162 Ok(*offset)
163 }
164}
165
166impl TryRead<'_> for ForwardedMessage {
167 fn try_read(bytes: &[u8], _ctx: ()) -> byte::Result<(Self, usize)> {
168 let offset = &mut 0;
169 let len: u8 = bytes.read(offset)?;
170 bytes.read::<u8>(offset)?; Ok((
172 ForwardedMessage {
173 ctrl: bytes.read(offset)?,
174 wireless_node_id: bytes.read_with(offset, len as usize - 3)?,
175 message: bytes.read(offset)?,
176 },
177 *offset,
178 ))
179 }
180}
181
182#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
183#[cfg_attr(feature = "defmt", derive(defmt::Format))]
184pub struct WirelessNodeId(heapless::String<16>);
185
186impl WirelessNodeId {
187 pub fn new() -> Self {
188 Self(String::new())
189 }
190}
191
192impl From<&str> for WirelessNodeId {
193 fn from(s: &str) -> Self {
194 Self(String::from(s))
195 }
196}
197
198impl Deref for WirelessNodeId {
199 type Target = heapless::String<16>;
200
201 fn deref(&self) -> &Self::Target {
202 &self.0
203 }
204}
205
206impl DerefMut for WirelessNodeId {
207 fn deref_mut(&mut self) -> &mut Self::Target {
208 &mut self.0
209 }
210}
211
212impl TryWrite for WirelessNodeId {
213 fn try_write(self, bytes: &mut [u8], _ctx: ()) -> byte::Result<usize> {
214 let offset = &mut 0;
215 bytes.write(offset, self.as_str())?;
216 Ok(*offset)
217 }
218}
219
220impl TryRead<'_, usize> for WirelessNodeId {
221 fn try_read(bytes: &[u8], len: usize) -> byte::Result<(Self, usize)> {
222 let offset = &mut 0;
223 let mut s = String::new();
224 s.push_str(bytes.read_with(offset, byte::ctx::Str::Len(len))?)
225 .map_err(|_e| byte::Error::BadInput {
226 err: "wireless_node_id longer than 16 bytes",
227 })?;
228 Ok((WirelessNodeId(s), *offset))
229 }
230}
231
232#[derive(Clone, Debug, PartialEq)]
233#[cfg_attr(feature = "defmt", derive(defmt::Format))]
234pub enum Message {
235 SearchGw(SearchGw),
236 GwInfo(GwInfo),
237 Connect(Connect),
238 ConnAck(ConnAck),
239 Register(Register),
240 RegAck(RegAck),
241 Publish(Publish),
242 PubAck(PubAck),
243 Subscribe(Subscribe),
244 SubAck(SubAck),
245 Unsubscribe(Unsubscribe),
246 UnsubAck(UnsubAck),
247 PingReq(PingReq),
248 PingResp(PingResp),
249}
250
251impl From<SearchGw> for Message {
252 fn from(msg: SearchGw) -> Self {
253 Message::SearchGw(msg)
254 }
255}
256
257impl From<GwInfo> for Message {
258 fn from(msg: GwInfo) -> Self {
259 Message::GwInfo(msg)
260 }
261}
262
263impl From<Connect> for Message {
264 fn from(msg: Connect) -> Self {
265 Message::Connect(msg)
266 }
267}
268
269impl From<ConnAck> for Message {
270 fn from(msg: ConnAck) -> Self {
271 Message::ConnAck(msg)
272 }
273}
274
275impl From<Register> for Message {
276 fn from(msg: Register) -> Self {
277 Message::Register(msg)
278 }
279}
280
281impl From<RegAck> for Message {
282 fn from(msg: RegAck) -> Self {
283 Message::RegAck(msg)
284 }
285}
286
287impl From<Publish> for Message {
288 fn from(msg: Publish) -> Self {
289 Message::Publish(msg)
290 }
291}
292
293impl From<PubAck> for Message {
294 fn from(msg: PubAck) -> Self {
295 Message::PubAck(msg)
296 }
297}
298
299impl From<Subscribe> for Message {
300 fn from(msg: Subscribe) -> Self {
301 Message::Subscribe(msg)
302 }
303}
304
305impl From<SubAck> for Message {
306 fn from(msg: SubAck) -> Self {
307 Message::SubAck(msg)
308 }
309}
310
311impl From<Unsubscribe> for Message {
312 fn from(msg: Unsubscribe) -> Self {
313 Message::Unsubscribe(msg)
314 }
315}
316
317impl From<UnsubAck> for Message {
318 fn from(msg: UnsubAck) -> Self {
319 Message::UnsubAck(msg)
320 }
321}
322
323impl From<PingReq> for Message {
324 fn from(msg: PingReq) -> Self {
325 Message::PingReq(msg)
326 }
327}
328
329impl From<PingResp> for Message {
330 fn from(msg: PingResp) -> Self {
331 Message::PingResp(msg)
332 }
333}
334
335impl TryWrite for Message {
336 fn try_write(self, bytes: &mut [u8], _ctx: ()) -> byte::Result<usize> {
337 let offset = &mut 0;
338 match self {
339 Message::SearchGw(msg) => bytes.write(offset, msg),
340 Message::GwInfo(msg) => bytes.write(offset, msg),
341 Message::Connect(msg) => bytes.write(offset, msg),
342 Message::ConnAck(msg) => bytes.write(offset, msg),
343 Message::Register(msg) => bytes.write(offset, msg),
344 Message::RegAck(msg) => bytes.write(offset, msg),
345 Message::Publish(msg) => bytes.write(offset, msg),
346 Message::PubAck(msg) => bytes.write(offset, msg),
347 Message::Subscribe(msg) => bytes.write(offset, msg),
348 Message::SubAck(msg) => bytes.write(offset, msg),
349 Message::Unsubscribe(msg) => bytes.write(offset, msg),
350 Message::UnsubAck(msg) => bytes.write(offset, msg),
351 Message::PingReq(msg) => bytes.write(offset, msg),
352 Message::PingResp(msg) => bytes.write(offset, msg),
353 }?;
354 Ok(*offset)
355 }
356}
357
358impl TryRead<'_> for Message {
359 fn try_read(bytes: &[u8], _ctx: ()) -> byte::Result<(Self, usize)> {
360 let offset = &mut 0;
361 Ok((
363 match bytes.read::<u8>(&mut (*offset + 1))? {
364 0x01 => Message::SearchGw(bytes.read(offset)?),
365 0x02 => Message::GwInfo(bytes.read(offset)?),
366 0x04 => Message::Connect(bytes.read(offset)?),
367 0x05 => Message::ConnAck(bytes.read(offset)?),
368 0x0a => Message::Register(bytes.read(offset)?),
369 0x0b => Message::RegAck(bytes.read(offset)?),
370 0x0c => Message::Publish(bytes.read(offset)?),
371 0x0d => Message::PubAck(bytes.read(offset)?),
372 Subscribe::MSG_TYPE => Message::Subscribe(bytes.read(offset)?),
373 SubAck::MSG_TYPE => Message::SubAck(bytes.read(offset)?),
374 Unsubscribe::MSG_TYPE => Message::Unsubscribe(bytes.read(offset)?),
375 UnsubAck::MSG_TYPE => Message::UnsubAck(bytes.read(offset)?),
376 0x16 => Message::PingReq(bytes.read(offset)?),
377 0x17 => Message::PingResp(bytes.read(offset)?),
378 _t => {
379 return Err(byte::Error::BadInput {
380 err: "Recieved a message with unknown type",
381 })
382 }
383 },
384 *offset,
385 ))
386 }
387}
388
389#[derive(Clone, Copy, Debug, PartialEq)]
390#[cfg_attr(feature = "defmt", derive(defmt::Format))]
391pub struct SearchGw {
392 pub radius: u8,
393}
394
395impl TryWrite for SearchGw {
396 fn try_write(self, bytes: &mut [u8], _ctx: ()) -> byte::Result<usize> {
397 let offset = &mut 0;
398 bytes.write(offset, 3u8)?; bytes.write(offset, 0x01u8)?; bytes.write(offset, self.radius)?;
401 Ok(*offset)
402 }
403}
404
405impl TryRead<'_> for SearchGw {
406 fn try_read(bytes: &[u8], _ctx: ()) -> byte::Result<(Self, usize)> {
407 let offset = &mut 0;
408 let len: u8 = bytes.read(offset)?;
409 check_len(bytes, len as usize)?;
410 *offset += 1; Ok((
412 SearchGw {
413 radius: bytes.read(offset)?,
414 },
415 *offset,
416 ))
417 }
418}
419
420#[derive(Clone, Copy, Debug, PartialEq)]
421#[cfg_attr(feature = "defmt", derive(defmt::Format))]
422pub struct GwInfo {
423 pub gw_id: u8,
424}
425
426impl TryWrite for GwInfo {
427 fn try_write(self, bytes: &mut [u8], _ctx: ()) -> byte::Result<usize> {
428 let offset = &mut 0;
429 bytes.write(offset, 3u8)?; bytes.write(offset, 0x02u8)?; bytes.write(offset, self.gw_id)?;
432 Ok(*offset)
433 }
434}
435
436impl TryRead<'_> for GwInfo {
437 fn try_read(bytes: &[u8], _ctx: ()) -> byte::Result<(Self, usize)> {
438 let offset = &mut 0;
439 let len: u8 = bytes.read(offset)?;
440 check_len(bytes, len as usize)?;
441 *offset += 1; Ok((
443 GwInfo {
444 gw_id: bytes.read(offset)?,
445 },
446 *offset,
447 ))
448 }
449}
450
451#[derive(Clone, Debug, PartialEq)]
452#[cfg_attr(feature = "defmt", derive(defmt::Format))]
453pub struct Connect {
454 pub flags: Flags,
455 pub duration: u16,
456 pub client_id: ClientId,
457}
458
459impl TryWrite for Connect {
460 fn try_write(self, bytes: &mut [u8], _ctx: ()) -> byte::Result<usize> {
461 let offset = &mut 0;
462 let len = 6 + self.client_id.len() as u8;
463 bytes.write(offset, len)?;
464 bytes.write(offset, 0x04u8)?; bytes.write(offset, self.flags)?;
466 bytes.write(offset, 0x01u8)?; bytes.write_with(offset, self.duration, byte::ctx::BE)?;
468 bytes.write(offset, self.client_id.as_str())?;
469 Ok(*offset)
470 }
471}
472
473impl TryRead<'_> for Connect {
474 fn try_read(bytes: &[u8], _ctx: ()) -> byte::Result<(Self, usize)> {
475 let offset = &mut 0;
476 let len: u8 = bytes.read(offset)?;
477 check_len(bytes, len as usize)?;
478 if len < 6 {
479 return Err(byte::Error::BadInput {
480 err: "Connect len must be >= 6 bytes",
481 });
482 }
483 *offset += 1; let flags = bytes.read(offset)?;
485 bytes.read::<u8>(offset)?; Ok((
487 Connect {
488 flags,
489 duration: bytes.read_with(offset, byte::ctx::BE)?,
490 client_id: bytes.read_with(offset, len as usize - 6)?,
491 },
492 *offset,
493 ))
494 }
495}
496
497#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
498#[cfg_attr(feature = "defmt", derive(defmt::Format))]
499pub struct ClientId(heapless::String<64>);
500
501impl ClientId {
502 pub fn new() -> Self {
503 Self(String::new())
504 }
505}
506
507impl From<&str> for ClientId {
508 fn from(s: &str) -> Self {
509 Self(String::from(s))
510 }
511}
512
513impl Deref for ClientId {
514 type Target = heapless::String<64>;
515
516 fn deref(&self) -> &Self::Target {
517 &self.0
518 }
519}
520
521impl DerefMut for ClientId {
522 fn deref_mut(&mut self) -> &mut Self::Target {
523 &mut self.0
524 }
525}
526
527impl TryWrite for ClientId {
528 fn try_write(self, bytes: &mut [u8], _ctx: ()) -> byte::Result<usize> {
529 let offset = &mut 0;
530 bytes.write(offset, self.as_str())?;
531 Ok(*offset)
532 }
533}
534
535impl TryRead<'_, usize> for ClientId {
536 fn try_read(bytes: &[u8], len: usize) -> byte::Result<(Self, usize)> {
537 let offset = &mut 0;
538 let mut s = String::new();
539 s.push_str(bytes.read_with(offset, byte::ctx::Str::Len(len))?)
540 .map_err(|_e| byte::Error::BadInput {
541 err: "client_id longer than 64 bytes",
542 })?;
543 Ok((ClientId(s), *offset))
544 }
545}
546
547#[derive(Clone, Copy, Debug, PartialEq)]
548#[cfg_attr(feature = "defmt", derive(defmt::Format))]
549pub struct ConnAck {
550 pub code: ReturnCode,
551}
552
553impl TryWrite for ConnAck {
554 fn try_write(self, bytes: &mut [u8], _ctx: ()) -> byte::Result<usize> {
555 let offset = &mut 0;
556 bytes.write(offset, 3u8)?; bytes.write(offset, 0x05u8)?; bytes.write(offset, self.code)?;
559 Ok(*offset)
560 }
561}
562
563impl TryRead<'_> for ConnAck {
564 fn try_read(bytes: &[u8], _ctx: ()) -> byte::Result<(Self, usize)> {
565 let offset = &mut 0;
566 let len: u8 = bytes.read(offset)?;
567 check_len(bytes, len as usize)?;
568 *offset += 1; Ok((
570 ConnAck {
571 code: bytes.read(offset)?,
572 },
573 *offset,
574 ))
575 }
576}
577
578#[derive(Clone, Debug, PartialEq)]
579#[cfg_attr(feature = "defmt", derive(defmt::Format))]
580pub struct Register {
581 pub topic_id: u16,
582 pub msg_id: u16,
583 pub topic_name: TopicName,
584}
585
586impl TryWrite for Register {
587 fn try_write(self, bytes: &mut [u8], _ctx: ()) -> byte::Result<usize> {
588 let offset = &mut 0;
589 let len = 6 + self.topic_name.len() as u8;
590 bytes.write(offset, len)?;
591 bytes.write(offset, 0x0Au8)?; bytes.write_with(offset, self.topic_id, byte::ctx::BE)?;
593 bytes.write_with(offset, self.msg_id, byte::ctx::BE)?;
594 bytes.write(offset, self.topic_name.as_str())?;
595 Ok(*offset)
596 }
597}
598
599impl TryRead<'_> for Register {
600 fn try_read(bytes: &[u8], _ctx: ()) -> byte::Result<(Self, usize)> {
601 let offset = &mut 0;
602 let len: u8 = bytes.read(offset)?;
603 check_len(bytes, len as usize)?;
604 if len < 6 {
605 return Err(byte::Error::BadInput {
606 err: "Register len must be >= 6 bytes",
607 });
608 }
609 *offset += 1; Ok((
611 Register {
612 topic_id: bytes.read_with(offset, byte::ctx::BE)?,
613 msg_id: bytes.read_with(offset, byte::ctx::BE)?,
614 topic_name: bytes.read_with(offset, len as usize - 6)?,
615 },
616 *offset,
617 ))
618 }
619}
620
621#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
622#[cfg_attr(feature = "defmt", derive(defmt::Format))]
623pub struct TopicName(heapless::String<256>);
624
625impl TopicName {
626 pub fn from(s: &str) -> Self {
627 Self(String::from(s))
628 }
629 pub fn new() -> Self {
630 Self(String::new())
631 }
632}
633
634impl Deref for TopicName {
635 type Target = heapless::String<256>;
636
637 fn deref(&self) -> &Self::Target {
638 &self.0
639 }
640}
641
642impl From<&str> for TopicName {
643 fn from(s: &str) -> Self {
644 Self(String::from(s))
645 }
646}
647
648impl DerefMut for TopicName {
649 fn deref_mut(&mut self) -> &mut Self::Target {
650 &mut self.0
651 }
652}
653
654impl TryWrite for TopicName {
655 fn try_write(self, bytes: &mut [u8], _ctx: ()) -> byte::Result<usize> {
656 let offset = &mut 0;
657 bytes.write(offset, self.as_str())?;
658 Ok(*offset)
659 }
660}
661
662impl TryRead<'_, usize> for TopicName {
663 fn try_read(bytes: &[u8], len: usize) -> byte::Result<(Self, usize)> {
664 let offset = &mut 0;
665 let mut s = String::new();
666 s.push_str(bytes.read_with(offset, byte::ctx::Str::Len(len))?)
667 .map_err(|_e| byte::Error::BadInput {
668 err: "topic_name longer than 256 bytes",
669 })?;
670 Ok((TopicName(s), *offset))
671 }
672}
673
674#[derive(Clone, Copy, Debug, PartialEq)]
675#[cfg_attr(feature = "defmt", derive(defmt::Format))]
676pub struct RegAck {
677 pub topic_id: u16,
678 pub msg_id: u16,
679 pub code: ReturnCode,
680}
681
682impl TryWrite for RegAck {
683 fn try_write(self, bytes: &mut [u8], _ctx: ()) -> byte::Result<usize> {
684 let offset = &mut 0;
685 bytes.write(offset, 7u8)?; bytes.write(offset, 0xBu8)?; bytes.write_with(offset, self.topic_id, byte::ctx::BE)?;
688 bytes.write_with(offset, self.msg_id, byte::ctx::BE)?;
689 bytes.write(offset, self.code)?;
690 Ok(*offset)
691 }
692}
693
694impl TryRead<'_> for RegAck {
695 fn try_read(bytes: &[u8], _ctx: ()) -> byte::Result<(Self, usize)> {
696 let offset = &mut 0;
697 let len: u8 = bytes.read(offset)?;
698 check_len(bytes, len as usize)?;
699 *offset += 1; Ok((
701 RegAck {
702 topic_id: bytes.read_with(offset, byte::ctx::BE)?,
703 msg_id: bytes.read_with(offset, byte::ctx::BE)?,
704 code: bytes.read(offset)?,
705 },
706 *offset,
707 ))
708 }
709}
710
711#[derive(Clone, Debug, PartialEq)]
712#[cfg_attr(feature = "defmt", derive(defmt::Format))]
713pub struct Publish {
714 pub flags: Flags,
715 pub topic_id: u16,
716 pub msg_id: u16,
717 pub data: PublishData,
718}
719
720impl TryWrite for Publish {
721 fn try_write(self, bytes: &mut [u8], _ctx: ()) -> byte::Result<usize> {
722 let offset = &mut 0;
723 let len = 7 + self.data.len() as u8;
724 bytes.write(offset, len)?;
725 bytes.write(offset, 0x0Cu8)?; bytes.write(offset, self.flags)?;
727 bytes.write_with(offset, self.topic_id, byte::ctx::BE)?;
728 bytes.write_with(offset, self.msg_id, byte::ctx::BE)?;
729 bytes.write(offset, self.data.as_str())?;
730 Ok(*offset)
731 }
732}
733
734impl TryRead<'_> for Publish {
735 fn try_read(bytes: &[u8], _ctx: ()) -> byte::Result<(Self, usize)> {
736 let offset = &mut 0;
737 let len: u8 = bytes.read(offset)?;
738 check_len(bytes, len as usize)?;
739 if len < 7 {
740 return Err(byte::Error::BadInput {
741 err: "Publish len must be >= 6 bytes",
742 });
743 }
744 *offset += 1; Ok((
746 Publish {
747 flags: bytes.read(offset)?,
748 topic_id: bytes.read_with(offset, byte::ctx::BE)?,
749 msg_id: bytes.read_with(offset, byte::ctx::BE)?,
750 data: bytes.read_with(offset, len as usize - 7)?,
751 },
752 *offset,
753 ))
754 }
755}
756
757#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
758#[cfg_attr(feature = "defmt", derive(defmt::Format))]
759pub struct PublishData(heapless::String<256>);
760
761impl PublishData {
762 pub fn new() -> Self {
763 Self(String::new())
764 }
765}
766
767impl From<&str> for PublishData {
768 fn from(s: &str) -> Self {
769 Self(String::from(s))
770 }
771}
772
773impl Deref for PublishData {
774 type Target = heapless::String<256>;
775
776 fn deref(&self) -> &Self::Target {
777 &self.0
778 }
779}
780
781impl DerefMut for PublishData {
782 fn deref_mut(&mut self) -> &mut Self::Target {
783 &mut self.0
784 }
785}
786
787impl TryWrite for PublishData {
788 fn try_write(self, bytes: &mut [u8], _ctx: ()) -> byte::Result<usize> {
789 let offset = &mut 0;
790 bytes.write(offset, self.as_str())?;
791 Ok(*offset)
792 }
793}
794
795impl TryRead<'_, usize> for PublishData {
796 fn try_read(bytes: &[u8], len: usize) -> byte::Result<(Self, usize)> {
797 let offset = &mut 0;
798 let mut s = String::new();
799 s.push_str(bytes.read_with(offset, byte::ctx::Str::Len(len))?)
800 .map_err(|_e| byte::Error::BadInput {
801 err: "data longer than 256 bytes",
802 })?;
803 Ok((PublishData(s), *offset))
804 }
805}
806
807#[derive(Clone, Debug, PartialEq)]
808#[cfg_attr(feature = "defmt", derive(defmt::Format))]
809pub struct PubAck {
810 pub topic_id: u16,
811 pub msg_id: u16,
812 pub code: ReturnCode,
813}
814
815impl TryWrite for PubAck {
816 fn try_write(self, bytes: &mut [u8], _ctx: ()) -> byte::Result<usize> {
817 let offset = &mut 0;
818 bytes.write(offset, 7u8)?; bytes.write(offset, 0x0Du8)?; bytes.write_with(offset, self.topic_id, byte::ctx::BE)?;
821 bytes.write_with(offset, self.msg_id, byte::ctx::BE)?;
822 bytes.write(offset, self.code)?;
823 Ok(*offset)
824 }
825}
826
827impl TryRead<'_> for PubAck {
828 fn try_read(bytes: &[u8], _ctx: ()) -> byte::Result<(Self, usize)> {
829 let offset = &mut 0;
830 let _len: u8 = bytes.read(offset)?;
831 *offset += 1; Ok((
833 PubAck {
834 topic_id: bytes.read_with(offset, byte::ctx::BE)?,
835 msg_id: bytes.read_with(offset, byte::ctx::BE)?,
836 code: bytes.read(offset)?,
837 },
838 *offset,
839 ))
840 }
841}
842
843#[derive(Clone, Debug, PartialEq)]
844#[cfg_attr(feature = "defmt", derive(defmt::Format))]
845pub enum TopicNameOrId {
846 Name(TopicName),
847 Id(u16),
848}
849
850impl TryWrite for TopicNameOrId {
851 fn try_write(self, bytes: &mut [u8], _ctx: ()) -> byte::Result<usize> {
852 let offset = &mut 0;
853 match self {
854 Self::Id(id) => bytes.write_with(offset, id, byte::ctx::BE)?,
855 Self::Name(name) => bytes.write(offset, name)?,
856 }
857 Ok(*offset)
858 }
859}
860
861impl TryRead<'_, (Flags, usize)> for TopicNameOrId {
862 fn try_read(bytes: &[u8], ctx: (Flags, usize)) -> byte::Result<(Self, usize)> {
863 let offset = &mut 0;
864 Ok((
865 if ctx.0.topic_id_type() == 0 {
866 Self::Name(bytes.read_with(offset, ctx.1)?)
867 } else {
868 Self::Id(bytes.read_with(offset, byte::ctx::BE)?)
869 },
870 *offset,
871 ))
872 }
873}
874
875#[derive(Clone, Debug, PartialEq)]
876#[cfg_attr(feature = "defmt", derive(defmt::Format))]
877pub struct Subscribe {
878 pub flags: Flags,
879 pub msg_id: u16,
880 pub topic: TopicNameOrId,
881}
882
883impl MsgType for Subscribe {
884 const MSG_TYPE: u8 = 0x12;
885}
886
887impl TryWrite for Subscribe {
888 fn try_write(self, bytes: &mut [u8], _ctx: ()) -> byte::Result<usize> {
889 let offset = &mut 1; bytes.write(offset, Self::MSG_TYPE)?;
891 bytes.write(offset, self.flags)?;
892 bytes.write_with(offset, self.msg_id, byte::ctx::BE)?;
893 bytes.write(offset, self.topic)?;
894 bytes.write(&mut 0, *offset as u8)?; Ok(*offset)
896 }
897}
898
899impl TryRead<'_> for Subscribe {
900 fn try_read(bytes: &[u8], _ctx: ()) -> byte::Result<(Self, usize)> {
901 let offset = &mut 0;
902 let len: u8 = bytes.read(offset)?;
903 *offset += 1; let flags = bytes.read(offset)?;
905 Ok((
906 Self {
907 flags,
908 msg_id: bytes.read_with(offset, byte::ctx::BE)?,
909 topic: bytes.read_with(offset, (flags, len as usize - 5))?,
910 },
911 *offset,
912 ))
913 }
914}
915
916#[derive(Clone, Debug, PartialEq)]
917#[cfg_attr(feature = "defmt", derive(defmt::Format))]
918pub struct SubAck {
919 pub flags: Flags,
920 pub msg_id: u16,
921 pub topic_id: u16,
922 pub code: ReturnCode,
923}
924
925impl MsgType for SubAck {
926 const MSG_TYPE: u8 = 0x13;
927}
928
929impl TryWrite for SubAck {
930 fn try_write(self, bytes: &mut [u8], _ctx: ()) -> byte::Result<usize> {
931 let offset = &mut 0;
932 bytes.write(offset, 7u8)?; bytes.write(offset, Self::MSG_TYPE)?;
934 bytes.write(offset, self.flags)?;
935 bytes.write_with(offset, self.topic_id, byte::ctx::BE)?;
936 bytes.write_with(offset, self.msg_id, byte::ctx::BE)?;
937 bytes.write(offset, self.code)?;
938 Ok(*offset)
939 }
940}
941
942impl TryRead<'_> for SubAck {
943 fn try_read(bytes: &[u8], _ctx: ()) -> byte::Result<(Self, usize)> {
944 let offset = &mut 0;
945 let _len: u8 = bytes.read(offset)?;
946 *offset += 1; Ok((
948 Self {
949 flags: bytes.read(offset)?,
950 topic_id: bytes.read_with(offset, byte::ctx::BE)?,
951 msg_id: bytes.read_with(offset, byte::ctx::BE)?,
952 code: bytes.read(offset)?,
953 },
954 *offset,
955 ))
956 }
957}
958
959#[derive(Clone, Debug, PartialEq)]
960#[cfg_attr(feature = "defmt", derive(defmt::Format))]
961pub struct Unsubscribe {
962 pub flags: Flags,
963 pub msg_id: u16,
964 pub topic: TopicNameOrId,
965}
966
967impl MsgType for Unsubscribe {
968 const MSG_TYPE: u8 = 0x14;
969}
970
971impl TryWrite for Unsubscribe {
972 fn try_write(self, bytes: &mut [u8], _ctx: ()) -> byte::Result<usize> {
973 let offset = &mut 1; bytes.write(offset, Self::MSG_TYPE)?;
975 bytes.write(offset, self.flags)?;
976 bytes.write_with(offset, self.msg_id, byte::ctx::BE)?;
977 bytes.write(offset, self.topic)?;
978 bytes.write(&mut 0, *offset as u8)?; Ok(*offset)
980 }
981}
982
983impl TryRead<'_> for Unsubscribe {
984 fn try_read(bytes: &[u8], _ctx: ()) -> byte::Result<(Self, usize)> {
985 let offset = &mut 0;
986 let len: u8 = bytes.read(offset)?;
987 *offset += 1; let flags = bytes.read(offset)?;
989 Ok((
990 Self {
991 flags,
992 msg_id: bytes.read_with(offset, byte::ctx::BE)?,
993 topic: bytes.read_with(offset, (flags, len as usize - 5))?,
994 },
995 *offset,
996 ))
997 }
998}
999
1000#[derive(Clone, Debug, PartialEq)]
1001#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1002pub struct UnsubAck {
1003 pub msg_id: u16,
1004 pub code: ReturnCode,
1005}
1006
1007impl MsgType for UnsubAck {
1008 const MSG_TYPE: u8 = 0x15;
1009}
1010
1011impl TryWrite for UnsubAck {
1012 fn try_write(self, bytes: &mut [u8], _ctx: ()) -> byte::Result<usize> {
1013 let offset = &mut 0;
1014 bytes.write(offset, 5u8)?; bytes.write(offset, Self::MSG_TYPE)?;
1016 bytes.write_with(offset, self.msg_id, byte::ctx::BE)?;
1017 bytes.write(offset, self.code)?;
1018 Ok(*offset)
1019 }
1020}
1021
1022impl TryRead<'_> for UnsubAck {
1023 fn try_read(bytes: &[u8], _ctx: ()) -> byte::Result<(Self, usize)> {
1024 let offset = &mut 0;
1025 let _len: u8 = bytes.read(offset)?;
1026 *offset += 1; Ok((
1028 Self {
1029 msg_id: bytes.read_with(offset, byte::ctx::BE)?,
1030 code: bytes.read(offset)?,
1031 },
1032 *offset,
1033 ))
1034 }
1035}
1036
1037#[derive(Clone, Debug, PartialEq)]
1038#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1039pub struct PingReq {
1040 pub client_id: ClientId,
1041}
1042
1043impl TryWrite for PingReq {
1044 fn try_write(self, bytes: &mut [u8], _ctx: ()) -> byte::Result<usize> {
1045 let offset = &mut 0;
1046 let len = 2 + self.client_id.len() as u8;
1047 bytes.write(offset, len)?;
1048 bytes.write(offset, 0x16u8)?; bytes.write(offset, self.client_id.as_str())?;
1050 Ok(*offset)
1051 }
1052}
1053
1054impl TryRead<'_> for PingReq {
1055 fn try_read(bytes: &[u8], _ctx: ()) -> byte::Result<(Self, usize)> {
1056 let offset = &mut 0;
1057 let len: u8 = bytes.read(offset)?;
1058 check_len(bytes, len as usize)?;
1059 if len < 2 {
1060 return Err(byte::Error::BadInput {
1061 err: "Len must be at least 2 bytes",
1062 });
1063 }
1064 *offset += 1; Ok((
1066 PingReq {
1067 client_id: bytes.read_with(offset, len as usize - 2)?,
1068 },
1069 *offset,
1070 ))
1071 }
1072}
1073
1074#[derive(Clone, Copy, Debug, PartialEq)]
1075#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1076pub struct PingResp {}
1077
1078impl TryWrite for PingResp {
1079 fn try_write(self, bytes: &mut [u8], _ctx: ()) -> byte::Result<usize> {
1080 let offset = &mut 0;
1081 bytes.write(offset, 2u8)?; bytes.write(offset, 0x17u8)?; Ok(*offset)
1084 }
1085}
1086
1087impl TryRead<'_> for PingResp {
1088 fn try_read(bytes: &[u8], _ctx: ()) -> byte::Result<(Self, usize)> {
1089 let offset = &mut 0;
1090 let len: u8 = bytes.read(offset)?;
1091 check_len(bytes, len as usize)?;
1092 *offset += 1; Ok((PingResp {}, *offset))
1094 }
1095}
1096
1097#[cfg(test)]
1098mod tests {
1099 use assert_hex::*;
1100
1101 use super::*;
1102
1103 #[test]
1104 fn forwarded_message_encode_parse() {
1105 let mut bytes = [0u8; 20];
1106 let mut len = 0usize;
1107 let expected = ForwardedMessage {
1108 ctrl: 0,
1109 wireless_node_id: WirelessNodeId::from("test-node"),
1110 message: Message::PingResp(PingResp {}),
1111 };
1112 bytes.write(&mut len, expected.clone()).unwrap();
1113 assert_eq_hex!(
1114 &bytes[..len],
1115 &[12u8, 0xfe, 0x00, b't', b'e', b's', b't', b'-', b'n', b'o', b'd', b'e', 2, 0x17]
1116 );
1117 let actual: ForwardedMessage = bytes.read(&mut 0).unwrap();
1118 assert_eq_hex!(actual, expected);
1119 }
1120
1121 #[test]
1122 fn return_code_encode() {
1123 let mut buf = [0u8; 5];
1124 let mut offset = 0usize;
1125 buf.write(&mut offset, ReturnCode::Accepted).unwrap();
1126 buf.write(
1127 &mut offset,
1128 ReturnCode::Rejected(RejectedReason::Congestion),
1129 )
1130 .unwrap();
1131 buf.write(
1132 &mut offset,
1133 ReturnCode::Rejected(RejectedReason::InvalidTopicId),
1134 )
1135 .unwrap();
1136 buf.write(
1137 &mut offset,
1138 ReturnCode::Rejected(RejectedReason::NotSupported),
1139 )
1140 .unwrap();
1141 buf.write(
1142 &mut offset,
1143 ReturnCode::Rejected(RejectedReason::Reserved(0x12)),
1144 )
1145 .unwrap();
1146 assert_eq_hex!(&buf, &[0x00u8, 0x01u8, 0x02u8, 0x03u8, 0x12u8]);
1147 }
1148
1149 #[test]
1150 fn return_code_parse() {
1151 let buf = &[0x00u8, 0x01u8, 0x02u8, 0x03u8, 0x12u8];
1152 let mut actual = [ReturnCode::Accepted; 5];
1153 let mut offset = 0usize;
1154 for i in 0..5 {
1155 actual[i] = buf.read(&mut offset).unwrap();
1156 }
1157 assert_eq!(
1158 &actual,
1159 &[
1160 ReturnCode::Accepted,
1161 ReturnCode::Rejected(RejectedReason::Congestion),
1162 ReturnCode::Rejected(RejectedReason::InvalidTopicId),
1163 ReturnCode::Rejected(RejectedReason::NotSupported),
1164 ReturnCode::Rejected(RejectedReason::Reserved(0x12)),
1165 ]
1166 );
1167 }
1168
1169 #[test]
1170 fn searchgw_encode_parse() {
1171 let bytes = &mut [0u8; 10];
1172 let mut len = 0usize;
1173 let expected = Message::SearchGw(SearchGw { radius: 5 });
1174 bytes.write(&mut len, expected.clone()).unwrap();
1175 assert_eq_hex!(&bytes[..len], [0x03u8, 0x01, 0x05]);
1176 let actual: Message = bytes.read(&mut 0).unwrap();
1177 assert_eq!(actual, expected);
1178 }
1179
1180 #[test]
1181 fn gwinfo_encode_parse() {
1182 let mut bytes = [0u8; 20];
1183 let mut len = 0usize;
1184 let expected = Message::GwInfo(GwInfo { gw_id: 0x12 });
1185 bytes.write(&mut len, expected.clone()).unwrap();
1186 assert_eq_hex!(&bytes[..len], [0x03u8, 0x02, 0x12]);
1187 let actual: Message = bytes.read(&mut 0).unwrap();
1188 assert_eq!(actual, expected);
1189 }
1190
1191 #[test]
1192 fn connect_encode_parse() {
1193 let mut bytes = [0u8; 20];
1194 let mut len = 0usize;
1195 let expected = Message::Connect(Connect {
1196 flags: Flags(0x12),
1197 duration: 0x3456,
1198 client_id: ClientId::from("test-client"),
1199 });
1200 bytes.write(&mut len, expected.clone()).unwrap();
1201 assert_eq_hex!(
1202 &bytes[..len],
1203 [
1204 0x11u8, 0x04, 0x12, 0x01, 0x34, 0x56, b't', b'e', b's', b't', b'-', b'c', b'l',
1205 b'i', b'e', b'n', b't'
1206 ]
1207 );
1208 let actual: Message = bytes.read(&mut 0).unwrap();
1209 assert_eq!(actual, expected);
1210 }
1211
1212 #[test]
1213 fn register_encode_parse() {
1214 let mut bytes = [0u8; 20];
1215 let mut len = 0usize;
1216 let expected = Message::Register(Register {
1217 topic_id: 0x1234,
1218 msg_id: 0x5678,
1219 topic_name: TopicName::from("test"),
1220 });
1221 bytes.write(&mut len, expected.clone()).unwrap();
1222 assert_eq_hex!(
1223 &bytes[..len],
1224 [0x0au8, 0x0a, 0x12, 0x34, 0x56, 0x78, b't', b'e', b's', b't']
1225 );
1226 let actual: Message = bytes.read(&mut 0).unwrap();
1227 assert_eq!(actual, expected);
1228 }
1229
1230 #[test]
1231 fn regack_encode_parse() {
1232 let mut bytes = [0u8; 20];
1233 let mut len = 0usize;
1234 let expected = Message::RegAck(RegAck {
1235 topic_id: 0x1234,
1236 msg_id: 0x5678,
1237 code: ReturnCode::Rejected(RejectedReason::Congestion),
1238 });
1239 bytes.write(&mut len, expected.clone()).unwrap();
1240 assert_eq_hex!(&bytes[..len], [0x07u8, 0x0b, 0x12, 0x34, 0x56, 0x78, 0x1]);
1241 let actual: Message = bytes.read(&mut 0).unwrap();
1242 assert_eq!(actual, expected);
1243 }
1244
1245 #[test]
1246 fn publish_encode_parse() {
1247 let mut bytes = [0u8; 20];
1248 let mut len = 0usize;
1249 let expected = Message::Publish(Publish {
1250 flags: Flags(0x12),
1251 topic_id: 0x1234,
1252 msg_id: 0x5678,
1253 data: PublishData::from("test"),
1254 });
1255 bytes.write(&mut len, expected.clone()).unwrap();
1256 assert_eq_hex!(
1257 &bytes[..len],
1258 [0x0bu8, 0x0c, 0x12, 0x12, 0x34, 0x56, 0x78, b't', b'e', b's', b't']
1259 );
1260 let actual: Message = bytes.read(&mut 0).unwrap();
1261 assert_eq!(actual, expected);
1262 }
1263
1264 #[test]
1265 fn puback_encode_parse() {
1266 let mut bytes = [0u8; 20];
1267 let mut len = 0usize;
1268 let expected = Message::PubAck(PubAck {
1269 topic_id: 0x1234,
1270 msg_id: 0x5678,
1271 code: RejectedReason::InvalidTopicId.into(),
1272 });
1273 bytes.write(&mut len, expected.clone()).unwrap();
1274 assert_eq_hex!(&bytes[..len], [0x07u8, 0x0d, 0x12, 0x34, 0x56, 0x78, 0x02]);
1275 let actual: Message = bytes.read(&mut 0).unwrap();
1276 assert_eq!(actual, expected);
1277 }
1278
1279 #[test]
1280 fn subscribe_encode_parse_id() {
1281 let mut bytes = [0u8; 20];
1282 let mut len = 0usize;
1283 let mut flags = Flags::default();
1284 flags.set_topic_id_type(0x2); let expected = Message::Subscribe(Subscribe {
1286 flags,
1287 msg_id: 0x1234,
1288 topic: TopicNameOrId::Id(0x5678),
1289 });
1290 bytes.write(&mut len, expected.clone()).unwrap();
1291 assert_eq_hex!(&bytes[..len], [0x07u8, 0x12, 0x02, 0x12, 0x34, 0x56, 0x78]);
1292 let actual: Message = bytes.read(&mut 0).unwrap();
1293 assert_eq!(actual, expected);
1294 }
1295
1296 #[test]
1297 fn subscribe_encode_parse_name() {
1298 let mut bytes = [0u8; 20];
1299 let mut len = 0usize;
1300 let expected = Message::Subscribe(Subscribe {
1301 flags: Flags::default(),
1302 msg_id: 0x1234,
1303 topic: TopicNameOrId::Name("test".into()),
1304 });
1305 bytes.write(&mut len, expected.clone()).unwrap();
1306 assert_eq_hex!(&bytes[..len], [0x09u8, 0x12, 0x00, 0x12, 0x34, b't', b'e', b's', b't']);
1307 let actual: Message = bytes.read(&mut 0).unwrap();
1308 assert_eq!(actual, expected);
1309 }
1310
1311 #[test]
1312 fn suback_encode_parse() {
1313 let mut bytes = [0u8; 20];
1314 let mut len = 0usize;
1315 let expected = Message::SubAck(SubAck {
1316 flags: Flags::default(),
1317 topic_id: 0x1234,
1318 msg_id: 0x5678,
1319 code: RejectedReason::InvalidTopicId.into(),
1320 });
1321 bytes.write(&mut len, expected.clone()).unwrap();
1322 assert_eq_hex!(&bytes[..len], [0x07u8, 0x13, 0x00, 0x12, 0x34, 0x56, 0x78, 0x02]);
1323 let actual: Message = bytes.read(&mut 0).unwrap();
1324 assert_eq!(actual, expected);
1325 }
1326 #[test]
1327 fn unsubscribe_encode_parse_id() {
1328 let mut bytes = [0u8; 20];
1329 let mut len = 0usize;
1330 let mut flags = Flags::default();
1331 flags.set_topic_id_type(0x2); let expected = Message::Unsubscribe(Unsubscribe {
1333 flags,
1334 msg_id: 0x1234,
1335 topic: TopicNameOrId::Id(0x5678),
1336 });
1337 bytes.write(&mut len, expected.clone()).unwrap();
1338 assert_eq_hex!(&bytes[..len], [0x07u8, 0x14, 0x02, 0x12, 0x34, 0x56, 0x78]);
1339 let actual: Message = bytes.read(&mut 0).unwrap();
1340 assert_eq!(actual, expected);
1341 }
1342
1343 #[test]
1344 fn unsubscribe_encode_parse_name() {
1345 let mut bytes = [0u8; 20];
1346 let mut len = 0usize;
1347 let expected = Message::Unsubscribe(Unsubscribe {
1348 flags: Flags::default(),
1349 msg_id: 0x1234,
1350 topic: TopicNameOrId::Name("test".into()),
1351 });
1352 bytes.write(&mut len, expected.clone()).unwrap();
1353 assert_eq_hex!(&bytes[..len], [0x09u8, 0x14, 0x00, 0x12, 0x34, b't', b'e', b's', b't']);
1354 let actual: Message = bytes.read(&mut 0).unwrap();
1355 assert_eq!(actual, expected);
1356 }
1357
1358 #[test]
1359 fn unsuback_encode_parse() {
1360 let mut bytes = [0u8; 20];
1361 let mut len = 0usize;
1362 let expected = Message::UnsubAck(UnsubAck {
1363 msg_id: 0x1234,
1364 code: RejectedReason::InvalidTopicId.into(),
1365 });
1366 bytes.write(&mut len, expected.clone()).unwrap();
1367 assert_eq_hex!(&bytes[..len], [0x05u8, 0x15, 0x12, 0x34, 0x02]);
1368 let actual: Message = bytes.read(&mut 0).unwrap();
1369 assert_eq!(actual, expected);
1370 }
1371
1372 #[test]
1373 fn pingreq_encode_parse() {
1374 let mut bytes = [0u8; 20];
1375 let mut len = 0usize;
1376 let expected = Message::PingReq(PingReq {
1377 client_id: ClientId::from("test-client"),
1378 });
1379 bytes.write(&mut len, expected.clone()).unwrap();
1380 assert_eq_hex!(
1381 &bytes[..len],
1382 [0xdu8, 0x16, b't', b'e', b's', b't', b'-', b'c', b'l', b'i', b'e', b'n', b't']
1383 );
1384 let actual: Message = bytes.read(&mut 0).unwrap();
1385 assert_eq!(actual, expected);
1386 }
1387
1388 #[test]
1389 fn pingresp_encode_parse() {
1390 let mut bytes = [0u8; 20];
1391 let _len = 0usize;
1392 let expected = Message::PingResp(PingResp {});
1393 let mut len = 0usize;
1394 bytes.write(&mut len, expected.clone()).unwrap();
1395 assert_eq_hex!(&bytes[..len], &[0x02u8, 0x17]);
1396 let actual: Message = bytes.read(&mut 0).unwrap();
1397 assert_eq!(actual, expected);
1398 }
1399}