1use crate::error::{Error, Result};
13use crate::length;
14use crate::tag::{self, ApduTag};
15use crate::traits::ApduDef;
16use broadcast_common::{Parse, Serialize};
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20#[cfg_attr(feature = "serde", derive(serde::Serialize))]
21#[non_exhaustive]
22pub enum CommsCommandId {
23 ConnectOnChannel,
25 DisconnectOnChannel,
27 SetParams,
29 EnquireStatus,
31 GetNextBuffer,
33 Reserved(u8),
35}
36
37impl CommsCommandId {
38 #[must_use]
40 pub fn from_u8(v: u8) -> Self {
41 match v {
42 0x01 => Self::ConnectOnChannel,
43 0x02 => Self::DisconnectOnChannel,
44 0x03 => Self::SetParams,
45 0x04 => Self::EnquireStatus,
46 0x05 => Self::GetNextBuffer,
47 other => Self::Reserved(other),
48 }
49 }
50 #[must_use]
52 pub const fn to_u8(self) -> u8 {
53 match self {
54 Self::ConnectOnChannel => 0x01,
55 Self::DisconnectOnChannel => 0x02,
56 Self::SetParams => 0x03,
57 Self::EnquireStatus => 0x04,
58 Self::GetNextBuffer => 0x05,
59 Self::Reserved(v) => v,
60 }
61 }
62 #[must_use]
64 pub fn name(&self) -> &'static str {
65 match self {
66 Self::ConnectOnChannel => "Connect_on_Channel",
67 Self::DisconnectOnChannel => "Disconnect_on_Channel",
68 Self::SetParams => "Set_Params",
69 Self::EnquireStatus => "Enquire_Status",
70 Self::GetNextBuffer => "Get_Next_Buffer",
71 Self::Reserved(_) => "reserved",
72 }
73 }
74}
75broadcast_common::impl_spec_display!(CommsCommandId, Reserved);
76
77#[derive(Debug, Clone, Copy, PartialEq, Eq)]
79#[cfg_attr(feature = "serde", derive(serde::Serialize))]
80#[non_exhaustive]
81pub enum ConnectionDescriptorType {
82 SiTelephoneDescriptor,
84 CableReturnChannelDescriptor,
86 Reserved(u8),
88}
89
90impl ConnectionDescriptorType {
91 #[must_use]
93 pub fn from_u8(v: u8) -> Self {
94 match v {
95 0x01 => Self::SiTelephoneDescriptor,
96 0x02 => Self::CableReturnChannelDescriptor,
97 other => Self::Reserved(other),
98 }
99 }
100 #[must_use]
102 pub const fn to_u8(self) -> u8 {
103 match self {
104 Self::SiTelephoneDescriptor => 0x01,
105 Self::CableReturnChannelDescriptor => 0x02,
106 Self::Reserved(v) => v,
107 }
108 }
109 #[must_use]
111 pub fn name(&self) -> &'static str {
112 match self {
113 Self::SiTelephoneDescriptor => "SI_Telephone_Descriptor",
114 Self::CableReturnChannelDescriptor => "Cable_Return_Channel_Descriptor",
115 Self::Reserved(_) => "reserved",
116 }
117 }
118}
119broadcast_common::impl_spec_display!(ConnectionDescriptorType, Reserved);
120
121#[derive(Debug, Clone, PartialEq, Eq)]
127#[cfg_attr(feature = "serde", derive(serde::Serialize))]
128pub struct ConnectionDescriptor<'a> {
129 pub descriptor_type: ConnectionDescriptorType,
131 #[cfg_attr(feature = "serde", serde(borrow, with = "super::bytes_serde"))]
134 pub payload: &'a [u8],
135}
136
137impl<'a> ConnectionDescriptor<'a> {
138 pub(crate) fn parse_component(bytes: &'a [u8]) -> Result<(Self, usize)> {
141 if bytes.len() < 3 {
142 return Err(Error::BufferTooShort {
143 need: 3,
144 have: bytes.len(),
145 what: "connection_descriptor tag",
146 });
147 }
148 let t = ApduTag::from_bytes(bytes[0], bytes[1], bytes[2]);
149 if t != tag::CONNECTION_DESCRIPTOR {
150 return Err(Error::UnexpectedApduTag {
151 got: t.as_u24(),
152 expected: tag::CONNECTION_DESCRIPTOR.as_u24(),
153 what: "connection_descriptor",
154 });
155 }
156 let (len_value, len_hdr) = length::decode(&bytes[3..])?;
157 let body_start = 3 + len_hdr;
158 let body_end = body_start + len_value;
159 if bytes.len() < body_end {
160 return Err(Error::LengthMismatch {
161 what: "connection_descriptor",
162 declared: len_value,
163 actual: bytes.len().saturating_sub(body_start),
164 });
165 }
166 let body = &bytes[body_start..body_end];
167 let type_byte = *body.first().ok_or(Error::BufferTooShort {
168 need: 1,
169 have: 0,
170 what: "connection_descriptor type",
171 })?;
172 Ok((
173 Self {
174 descriptor_type: ConnectionDescriptorType::from_u8(type_byte),
175 payload: &body[1..],
176 },
177 body_end,
178 ))
179 }
180
181 pub(crate) fn component_len(&self) -> usize {
182 super::apdu_len(1 + self.payload.len())
183 }
184
185 pub(crate) fn serialize_component(&self, buf: &mut [u8]) -> Result<usize> {
186 let body_len = 1 + self.payload.len();
187 let mut pos = super::write_apdu_header(tag::CONNECTION_DESCRIPTOR, body_len, buf)?;
188 buf[pos] = self.descriptor_type.to_u8();
189 pos += 1;
190 buf[pos..pos + self.payload.len()].copy_from_slice(self.payload);
191 pos += self.payload.len();
192 Ok(pos)
193 }
194}
195
196impl<'a> Parse<'a> for ConnectionDescriptor<'a> {
197 type Error = Error;
198 fn parse(bytes: &'a [u8]) -> Result<Self> {
199 let (c, consumed) = Self::parse_component(bytes)?;
200 if consumed != bytes.len() {
201 return Err(Error::LengthMismatch {
202 what: "connection_descriptor",
203 declared: consumed,
204 actual: bytes.len(),
205 });
206 }
207 Ok(c)
208 }
209}
210
211impl Serialize for ConnectionDescriptor<'_> {
212 type Error = Error;
213 fn serialized_len(&self) -> usize {
214 self.component_len()
215 }
216 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
217 self.serialize_component(buf)
218 }
219}
220
221impl<'a> ApduDef<'a> for ConnectionDescriptor<'a> {
222 const TAG: ApduTag = tag::CONNECTION_DESCRIPTOR;
223 const NAME: &'static str = "CONNECTION_DESCRIPTOR";
224}
225
226#[derive(Debug, Clone, PartialEq, Eq)]
228#[cfg_attr(feature = "serde", derive(serde::Serialize))]
229#[non_exhaustive]
230pub enum CommsCmdParams<'a> {
231 Connect {
233 #[cfg_attr(feature = "serde", serde(borrow))]
235 connection_descriptor: ConnectionDescriptor<'a>,
236 retry_count: u8,
238 timeout: u8,
240 },
241 SetParams {
243 buffer_size: u8,
245 timeout: u8,
247 },
248 GetNextBuffer {
250 comms_phase_id: u8,
252 },
253 None,
256}
257
258#[derive(Debug, Clone, PartialEq, Eq)]
260#[cfg_attr(feature = "serde", derive(serde::Serialize))]
261pub struct CommsCmd<'a> {
262 pub command_id: CommsCommandId,
264 #[cfg_attr(feature = "serde", serde(borrow))]
266 pub params: CommsCmdParams<'a>,
267}
268
269impl<'a> Parse<'a> for CommsCmd<'a> {
270 type Error = Error;
271 fn parse(bytes: &'a [u8]) -> Result<Self> {
272 let body = super::parse_apdu_header(bytes, tag::COMMS_CMD, "comms_cmd")?;
273 let id_byte = *body.first().ok_or(Error::BufferTooShort {
274 need: 1,
275 have: 0,
276 what: "comms_cmd command_id",
277 })?;
278 let command_id = CommsCommandId::from_u8(id_byte);
279 let rest = &body[1..];
280 let params = match command_id {
281 CommsCommandId::ConnectOnChannel => {
282 let (cd, consumed) = ConnectionDescriptor::parse_component(rest)?;
283 let tail = &rest[consumed..];
284 if tail.len() < 2 {
285 return Err(Error::BufferTooShort {
286 need: 2,
287 have: tail.len(),
288 what: "comms_cmd connect retry/timeout",
289 });
290 }
291 CommsCmdParams::Connect {
292 connection_descriptor: cd,
293 retry_count: tail[0],
294 timeout: tail[1],
295 }
296 }
297 CommsCommandId::SetParams => {
298 if rest.len() < 2 {
299 return Err(Error::BufferTooShort {
300 need: 2,
301 have: rest.len(),
302 what: "comms_cmd set_params",
303 });
304 }
305 CommsCmdParams::SetParams {
306 buffer_size: rest[0],
307 timeout: rest[1],
308 }
309 }
310 CommsCommandId::GetNextBuffer => {
311 let comms_phase_id = *rest.first().ok_or(Error::BufferTooShort {
312 need: 1,
313 have: 0,
314 what: "comms_cmd get_next_buffer",
315 })?;
316 CommsCmdParams::GetNextBuffer { comms_phase_id }
317 }
318 _ => CommsCmdParams::None,
319 };
320 Ok(Self { command_id, params })
321 }
322}
323
324impl CommsCmd<'_> {
325 fn body_len(&self) -> usize {
326 1 + match &self.params {
327 CommsCmdParams::Connect {
328 connection_descriptor,
329 ..
330 } => connection_descriptor.component_len() + 2,
331 CommsCmdParams::SetParams { .. } => 2,
332 CommsCmdParams::GetNextBuffer { .. } => 1,
333 CommsCmdParams::None => 0,
334 }
335 }
336}
337
338impl Serialize for CommsCmd<'_> {
339 type Error = Error;
340 fn serialized_len(&self) -> usize {
341 super::apdu_len(self.body_len())
342 }
343 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
344 let body_len = self.body_len();
345 let mut pos = super::write_apdu_header(tag::COMMS_CMD, body_len, buf)?;
346 buf[pos] = self.command_id.to_u8();
347 pos += 1;
348 match &self.params {
349 CommsCmdParams::Connect {
350 connection_descriptor,
351 retry_count,
352 timeout,
353 } => {
354 pos += connection_descriptor.serialize_component(&mut buf[pos..])?;
355 buf[pos] = *retry_count;
356 buf[pos + 1] = *timeout;
357 pos += 2;
358 }
359 CommsCmdParams::SetParams {
360 buffer_size,
361 timeout,
362 } => {
363 buf[pos] = *buffer_size;
364 buf[pos + 1] = *timeout;
365 pos += 2;
366 }
367 CommsCmdParams::GetNextBuffer { comms_phase_id } => {
368 buf[pos] = *comms_phase_id;
369 pos += 1;
370 }
371 CommsCmdParams::None => {}
372 }
373 Ok(pos)
374 }
375}
376
377impl<'a> ApduDef<'a> for CommsCmd<'a> {
378 const TAG: ApduTag = tag::COMMS_CMD;
379 const NAME: &'static str = "COMMS_CMD";
380}
381
382#[derive(Debug, Clone, Copy, PartialEq, Eq)]
384#[cfg_attr(feature = "serde", derive(serde::Serialize))]
385#[non_exhaustive]
386pub enum CommsReplyId {
387 ConnectAck,
389 DisconnectAck,
391 SetParamsAck,
393 StatusReply,
395 GetNextBufferAck,
397 SendAck,
399 Reserved(u8),
401}
402
403impl CommsReplyId {
404 #[must_use]
406 pub fn from_u8(v: u8) -> Self {
407 match v {
408 0x01 => Self::ConnectAck,
409 0x02 => Self::DisconnectAck,
410 0x03 => Self::SetParamsAck,
411 0x04 => Self::StatusReply,
412 0x05 => Self::GetNextBufferAck,
413 0x06 => Self::SendAck,
414 other => Self::Reserved(other),
415 }
416 }
417 #[must_use]
419 pub const fn to_u8(self) -> u8 {
420 match self {
421 Self::ConnectAck => 0x01,
422 Self::DisconnectAck => 0x02,
423 Self::SetParamsAck => 0x03,
424 Self::StatusReply => 0x04,
425 Self::GetNextBufferAck => 0x05,
426 Self::SendAck => 0x06,
427 Self::Reserved(v) => v,
428 }
429 }
430 #[must_use]
432 pub fn name(&self) -> &'static str {
433 match self {
434 Self::ConnectAck => "Connect_Ack",
435 Self::DisconnectAck => "Disconnect_Ack",
436 Self::SetParamsAck => "Set_Params_Ack",
437 Self::StatusReply => "Status_Reply",
438 Self::GetNextBufferAck => "Get_Next_Buffer_Ack",
439 Self::SendAck => "Send_Ack",
440 Self::Reserved(_) => "reserved",
441 }
442 }
443}
444broadcast_common::impl_spec_display!(CommsReplyId, Reserved);
445
446#[derive(Debug, Clone, Copy, PartialEq, Eq)]
448#[cfg_attr(feature = "serde", derive(serde::Serialize))]
449pub struct CommsReply {
450 pub reply_id: CommsReplyId,
452 pub return_value: u8,
454}
455
456const COMMS_REPLY_BODY: usize = 2;
458
459impl<'a> Parse<'a> for CommsReply {
460 type Error = Error;
461 fn parse(bytes: &'a [u8]) -> Result<Self> {
462 let body = super::parse_apdu_header(bytes, tag::COMMS_REPLY, "comms_reply")?;
463 if body.len() < COMMS_REPLY_BODY {
464 return Err(Error::BufferTooShort {
465 need: COMMS_REPLY_BODY,
466 have: body.len(),
467 what: "comms_reply",
468 });
469 }
470 Ok(Self {
471 reply_id: CommsReplyId::from_u8(body[0]),
472 return_value: body[1],
473 })
474 }
475}
476
477impl Serialize for CommsReply {
478 type Error = Error;
479 fn serialized_len(&self) -> usize {
480 super::apdu_len(COMMS_REPLY_BODY)
481 }
482 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
483 let mut pos = super::write_apdu_header(tag::COMMS_REPLY, COMMS_REPLY_BODY, buf)?;
484 buf[pos] = self.reply_id.to_u8();
485 buf[pos + 1] = self.return_value;
486 pos += COMMS_REPLY_BODY;
487 Ok(pos)
488 }
489}
490
491impl ApduDef<'_> for CommsReply {
492 const TAG: ApduTag = tag::COMMS_REPLY;
493 const NAME: &'static str = "COMMS_REPLY";
494}
495
496#[derive(Debug, Clone, PartialEq, Eq)]
501#[cfg_attr(feature = "serde", derive(serde::Serialize))]
502pub struct CommsSend<'a> {
503 pub more: bool,
505 pub comms_phase_id: u8,
507 #[cfg_attr(feature = "serde", serde(borrow, with = "super::bytes_serde"))]
509 pub message: &'a [u8],
510}
511
512impl CommsSend<'_> {
513 #[must_use]
515 pub fn tag(&self) -> ApduTag {
516 if self.more {
517 tag::COMMS_SEND_MORE
518 } else {
519 tag::COMMS_SEND_LAST
520 }
521 }
522}
523
524impl<'a> Parse<'a> for CommsSend<'a> {
525 type Error = Error;
526 fn parse(bytes: &'a [u8]) -> Result<Self> {
527 if bytes.len() < 3 {
528 return Err(Error::BufferTooShort {
529 need: 3,
530 have: bytes.len(),
531 what: "comms_send tag",
532 });
533 }
534 let t = ApduTag::from_bytes(bytes[0], bytes[1], bytes[2]);
535 let (expected, more) = match t {
536 tag::COMMS_SEND_MORE => (tag::COMMS_SEND_MORE, true),
537 _ => (tag::COMMS_SEND_LAST, false),
538 };
539 let body = super::parse_apdu_header(bytes, expected, "comms_send")?;
540 let comms_phase_id = *body.first().ok_or(Error::BufferTooShort {
541 need: 1,
542 have: 0,
543 what: "comms_send comms_phase_id",
544 })?;
545 Ok(Self {
546 more,
547 comms_phase_id,
548 message: &body[1..],
549 })
550 }
551}
552
553impl Serialize for CommsSend<'_> {
554 type Error = Error;
555 fn serialized_len(&self) -> usize {
556 super::apdu_len(1 + self.message.len())
557 }
558 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
559 let body_len = 1 + self.message.len();
560 let mut pos = super::write_apdu_header(self.tag(), body_len, buf)?;
561 buf[pos] = self.comms_phase_id;
562 pos += 1;
563 buf[pos..pos + self.message.len()].copy_from_slice(self.message);
564 pos += self.message.len();
565 Ok(pos)
566 }
567}
568
569impl<'a> ApduDef<'a> for CommsSend<'a> {
570 const TAG: ApduTag = tag::COMMS_SEND_LAST;
571 const NAME: &'static str = "COMMS_SEND";
572}
573
574#[derive(Debug, Clone, PartialEq, Eq)]
579#[cfg_attr(feature = "serde", derive(serde::Serialize))]
580pub struct CommsRcv<'a> {
581 pub more: bool,
583 pub comms_phase_id: u8,
585 #[cfg_attr(feature = "serde", serde(borrow, with = "super::bytes_serde"))]
587 pub message: &'a [u8],
588}
589
590impl CommsRcv<'_> {
591 #[must_use]
593 pub fn tag(&self) -> ApduTag {
594 if self.more {
595 tag::COMMS_RCV_MORE
596 } else {
597 tag::COMMS_RCV_LAST
598 }
599 }
600}
601
602impl<'a> Parse<'a> for CommsRcv<'a> {
603 type Error = Error;
604 fn parse(bytes: &'a [u8]) -> Result<Self> {
605 if bytes.len() < 3 {
606 return Err(Error::BufferTooShort {
607 need: 3,
608 have: bytes.len(),
609 what: "comms_rcv tag",
610 });
611 }
612 let t = ApduTag::from_bytes(bytes[0], bytes[1], bytes[2]);
613 let (expected, more) = match t {
614 tag::COMMS_RCV_MORE => (tag::COMMS_RCV_MORE, true),
615 _ => (tag::COMMS_RCV_LAST, false),
616 };
617 let body = super::parse_apdu_header(bytes, expected, "comms_rcv")?;
618 let comms_phase_id = *body.first().ok_or(Error::BufferTooShort {
619 need: 1,
620 have: 0,
621 what: "comms_rcv comms_phase_id",
622 })?;
623 Ok(Self {
624 more,
625 comms_phase_id,
626 message: &body[1..],
627 })
628 }
629}
630
631impl Serialize for CommsRcv<'_> {
632 type Error = Error;
633 fn serialized_len(&self) -> usize {
634 super::apdu_len(1 + self.message.len())
635 }
636 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
637 let body_len = 1 + self.message.len();
638 let mut pos = super::write_apdu_header(self.tag(), body_len, buf)?;
639 buf[pos] = self.comms_phase_id;
640 pos += 1;
641 buf[pos..pos + self.message.len()].copy_from_slice(self.message);
642 pos += self.message.len();
643 Ok(pos)
644 }
645}
646
647impl<'a> ApduDef<'a> for CommsRcv<'a> {
648 const TAG: ApduTag = tag::COMMS_RCV_LAST;
649 const NAME: &'static str = "COMMS_RCV";
650}
651
652#[cfg(test)]
653mod tests {
654 use super::*;
655
656 #[test]
657 fn comms_cmd_connect_round_trips_and_bites() {
658 let cmd = CommsCmd {
659 command_id: CommsCommandId::ConnectOnChannel,
660 params: CommsCmdParams::Connect {
661 connection_descriptor: ConnectionDescriptor {
662 descriptor_type: ConnectionDescriptorType::CableReturnChannelDescriptor,
663 payload: &[0x07], },
665 retry_count: 3,
666 timeout: 30,
667 },
668 };
669 let bytes = cmd.to_bytes();
670 assert_eq!(
672 bytes,
673 [
674 0x9F, 0x8C, 0x00, 0x09, 0x01, 0x9F, 0x8C, 0x01, 0x02, 0x02, 0x07, 0x03, 0x1E
675 ]
676 );
677 assert_eq!(CommsCmd::parse(&bytes).unwrap(), cmd);
678
679 let mut other = cmd.clone();
680 other.params = CommsCmdParams::Connect {
681 connection_descriptor: ConnectionDescriptor {
682 descriptor_type: ConnectionDescriptorType::CableReturnChannelDescriptor,
683 payload: &[0x08],
684 },
685 retry_count: 3,
686 timeout: 30,
687 };
688 assert_ne!(bytes, other.to_bytes());
689 }
690
691 #[test]
692 fn comms_cmd_set_params_and_get_next() {
693 let sp = CommsCmd {
694 command_id: CommsCommandId::SetParams,
695 params: CommsCmdParams::SetParams {
696 buffer_size: 254,
697 timeout: 10,
698 },
699 };
700 let bytes = sp.to_bytes();
701 assert_eq!(bytes, [0x9F, 0x8C, 0x00, 0x03, 0x03, 0xFE, 0x0A]);
702 assert_eq!(CommsCmd::parse(&bytes).unwrap(), sp);
703 assert_eq!(sp.command_id.name(), "Set_Params");
704
705 let gnb = CommsCmd {
706 command_id: CommsCommandId::GetNextBuffer,
707 params: CommsCmdParams::GetNextBuffer { comms_phase_id: 1 },
708 };
709 let gb = gnb.to_bytes();
710 assert_eq!(gb, [0x9F, 0x8C, 0x00, 0x02, 0x05, 0x01]);
711 assert_eq!(CommsCmd::parse(&gb).unwrap(), gnb);
712 }
713
714 #[test]
715 fn comms_cmd_disconnect_has_no_params() {
716 let d = CommsCmd {
717 command_id: CommsCommandId::DisconnectOnChannel,
718 params: CommsCmdParams::None,
719 };
720 let bytes = d.to_bytes();
721 assert_eq!(bytes, [0x9F, 0x8C, 0x00, 0x01, 0x02]);
722 assert_eq!(CommsCmd::parse(&bytes).unwrap(), d);
723 }
724
725 #[test]
726 fn connection_descriptor_standalone_round_trips() {
727 let cd = ConnectionDescriptor {
728 descriptor_type: ConnectionDescriptorType::SiTelephoneDescriptor,
729 payload: &[0xAA, 0xBB, 0xCC],
730 };
731 let bytes = cd.to_bytes();
732 assert_eq!(bytes, [0x9F, 0x8C, 0x01, 0x04, 0x01, 0xAA, 0xBB, 0xCC]);
733 assert_eq!(ConnectionDescriptor::parse(&bytes).unwrap(), cd);
734 let mut other = cd.clone();
735 other.descriptor_type = ConnectionDescriptorType::CableReturnChannelDescriptor;
736 assert_ne!(bytes, other.to_bytes());
737 }
738
739 #[test]
740 fn comms_reply_round_trips_and_bites() {
741 let r = CommsReply {
742 reply_id: CommsReplyId::StatusReply,
743 return_value: 0x01, };
745 let bytes = r.to_bytes();
746 assert_eq!(bytes, [0x9F, 0x8C, 0x02, 0x02, 0x04, 0x01]);
747 assert_eq!(CommsReply::parse(&bytes).unwrap(), r);
748 assert_eq!(r.reply_id.name(), "Status_Reply");
749 let mut other = r;
750 other.return_value = 0x00;
751 assert_ne!(bytes, other.to_bytes());
752 }
753
754 #[test]
755 fn comms_send_multibyte_round_trips_and_more_bites() {
756 let s = CommsSend {
757 more: false,
758 comms_phase_id: 0,
759 message: b"AT&F\r",
760 };
761 let bytes = s.to_bytes();
762 assert_eq!(
763 bytes,
764 [0x9F, 0x8C, 0x03, 0x06, 0x00, b'A', b'T', b'&', b'F', b'\r']
765 );
766 assert_eq!(CommsSend::parse(&bytes).unwrap(), s);
767
768 let mut more = s.clone();
770 more.more = true;
771 let mb = more.to_bytes();
772 assert_eq!(mb[2], 0x04);
773 assert_ne!(bytes, mb);
774 assert_eq!(CommsSend::parse(&mb).unwrap(), more);
775
776 let mut other = s.clone();
778 other.comms_phase_id = 1;
779 assert_ne!(bytes, other.to_bytes());
780 }
781
782 #[test]
783 fn comms_rcv_round_trips_and_more_bites() {
784 let r = CommsRcv {
785 more: true,
786 comms_phase_id: 1,
787 message: b"OK\r\n",
788 };
789 let bytes = r.to_bytes();
790 assert_eq!(bytes[2], 0x06); assert_eq!(CommsRcv::parse(&bytes).unwrap(), r);
792
793 let mut last = r.clone();
794 last.more = false;
795 let lb = last.to_bytes();
796 assert_eq!(lb[2], 0x05); assert_ne!(bytes, lb);
798 assert_eq!(CommsRcv::parse(&lb).unwrap(), last);
799 }
800}