1use crate::types::RosMessage;
35use core::fmt;
36use nros_serdes::{CdrReader, CdrWriter, DeserError, Deserialize, SerError, Serialize};
37
38pub trait RosAction: Sized {
54 type Goal: RosMessage;
56
57 type Result: RosMessage + Default;
59
60 type Feedback: RosMessage;
62
63 type SendGoalRequest: RosMessage;
65
66 type SendGoalResponse: RosMessage;
68
69 type GetResultRequest: RosMessage;
71
72 type GetResultResponse: RosMessage;
74
75 type FeedbackMessage: RosMessage;
77
78 const ACTION_NAME: &'static str;
80
81 const ACTION_HASH: &'static str;
83
84 const SEND_GOAL_SERVICE_HASH: &'static str = Self::ACTION_HASH;
93
94 const GET_RESULT_SERVICE_HASH: &'static str = Self::ACTION_HASH;
97
98 const CANCEL_GOAL_SERVICE_HASH: &'static str = Self::ACTION_HASH;
104
105 const STATUS_MESSAGE_HASH: &'static str = Self::ACTION_HASH;
108
109 #[allow(clippy::result_unit_err)]
125 fn register_protocol_types() -> Result<(), ()> {
126 Ok(())
127 }
128}
129
130#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
135#[repr(i8)]
136pub enum GoalStatus {
137 #[default]
139 Unknown = 0,
140 Accepted = 1,
142 Executing = 2,
144 Canceling = 3,
146 Succeeded = 4,
148 Canceled = 5,
150 Aborted = 6,
152}
153
154impl GoalStatus {
155 pub fn is_terminal(&self) -> bool {
157 matches!(
158 self,
159 GoalStatus::Succeeded | GoalStatus::Canceled | GoalStatus::Aborted
160 )
161 }
162
163 pub fn is_active(&self) -> bool {
165 matches!(
166 self,
167 GoalStatus::Accepted | GoalStatus::Executing | GoalStatus::Canceling
168 )
169 }
170
171 pub fn from_i8(value: i8) -> Option<Self> {
173 match value {
174 0 => Some(GoalStatus::Unknown),
175 1 => Some(GoalStatus::Accepted),
176 2 => Some(GoalStatus::Executing),
177 3 => Some(GoalStatus::Canceling),
178 4 => Some(GoalStatus::Succeeded),
179 5 => Some(GoalStatus::Canceled),
180 6 => Some(GoalStatus::Aborted),
181 _ => None,
182 }
183 }
184}
185
186impl fmt::Display for GoalStatus {
187 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
188 match self {
189 GoalStatus::Unknown => write!(f, "UNKNOWN"),
190 GoalStatus::Accepted => write!(f, "ACCEPTED"),
191 GoalStatus::Executing => write!(f, "EXECUTING"),
192 GoalStatus::Canceling => write!(f, "CANCELING"),
193 GoalStatus::Succeeded => write!(f, "SUCCEEDED"),
194 GoalStatus::Canceled => write!(f, "CANCELED"),
195 GoalStatus::Aborted => write!(f, "ABORTED"),
196 }
197 }
198}
199
200impl Serialize for GoalStatus {
201 fn serialize(&self, writer: &mut CdrWriter) -> Result<(), SerError> {
202 writer.write_i8(*self as i8)
203 }
204}
205
206impl Deserialize for GoalStatus {
207 fn deserialize(reader: &mut CdrReader) -> Result<Self, DeserError> {
208 let value = reader.read_i8()?;
209 GoalStatus::from_i8(value).ok_or(DeserError::InvalidData)
210 }
211}
212
213#[derive(Clone, Copy, PartialEq, Eq, Hash)]
217pub struct GoalId {
218 pub uuid: [u8; 16],
220}
221
222impl GoalId {
223 pub const UUID_LEN: usize = 16;
225
226 pub const SEQ_PREFIX_LEN: usize = 0;
237
238 pub const fn new(uuid: [u8; 16]) -> Self {
240 Self { uuid }
241 }
242
243 pub const fn zero() -> Self {
245 Self { uuid: [0; 16] }
246 }
247
248 pub fn is_zero(&self) -> bool {
250 self.uuid == [0; 16]
251 }
252
253 pub fn from_counter(counter: u64) -> Self {
258 let mut uuid = [0u8; 16];
259 uuid[8..16].copy_from_slice(&counter.to_be_bytes());
261 uuid[6] = (uuid[6] & 0x0f) | 0x40; uuid[8] = (uuid[8] & 0x3f) | 0x80; Self { uuid }
265 }
266}
267
268impl Default for GoalId {
269 fn default() -> Self {
270 Self::zero()
271 }
272}
273
274impl Serialize for GoalId {
275 fn serialize(&self, writer: &mut CdrWriter) -> Result<(), SerError> {
276 for byte in &self.uuid {
277 writer.write_u8(*byte)?;
278 }
279 Ok(())
280 }
281}
282
283impl Deserialize for GoalId {
284 fn deserialize(reader: &mut CdrReader) -> Result<Self, DeserError> {
285 let mut uuid = [0u8; 16];
286 for byte in &mut uuid {
287 *byte = reader.read_u8()?;
288 }
289 Ok(Self { uuid })
290 }
291}
292
293impl fmt::Debug for GoalId {
294 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
295 write!(
297 f,
298 "GoalId({:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x})",
299 self.uuid[0],
300 self.uuid[1],
301 self.uuid[2],
302 self.uuid[3],
303 self.uuid[4],
304 self.uuid[5],
305 self.uuid[6],
306 self.uuid[7],
307 self.uuid[8],
308 self.uuid[9],
309 self.uuid[10],
310 self.uuid[11],
311 self.uuid[12],
312 self.uuid[13],
313 self.uuid[14],
314 self.uuid[15]
315 )
316 }
317}
318
319impl fmt::Display for GoalId {
320 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
321 write!(
322 f,
323 "{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
324 self.uuid[0],
325 self.uuid[1],
326 self.uuid[2],
327 self.uuid[3],
328 self.uuid[4],
329 self.uuid[5],
330 self.uuid[6],
331 self.uuid[7],
332 self.uuid[8],
333 self.uuid[9],
334 self.uuid[10],
335 self.uuid[11],
336 self.uuid[12],
337 self.uuid[13],
338 self.uuid[14],
339 self.uuid[15]
340 )
341 }
342}
343
344#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
349pub struct GoalInfo {
350 pub goal_id: GoalId,
352 pub stamp_sec: i32,
354 pub stamp_nanosec: u32,
356}
357
358impl GoalInfo {
359 pub const fn new(goal_id: GoalId, stamp_sec: i32, stamp_nanosec: u32) -> Self {
361 Self {
362 goal_id,
363 stamp_sec,
364 stamp_nanosec,
365 }
366 }
367
368 pub const fn with_id(goal_id: GoalId) -> Self {
370 Self {
371 goal_id,
372 stamp_sec: 0,
373 stamp_nanosec: 0,
374 }
375 }
376}
377
378impl Serialize for GoalInfo {
379 fn serialize(&self, writer: &mut CdrWriter) -> Result<(), SerError> {
380 self.goal_id.serialize(writer)?;
381 writer.write_i32(self.stamp_sec)?;
382 writer.write_u32(self.stamp_nanosec)?;
383 Ok(())
384 }
385}
386
387impl Deserialize for GoalInfo {
388 fn deserialize(reader: &mut CdrReader) -> Result<Self, DeserError> {
389 Ok(Self {
390 goal_id: GoalId::deserialize(reader)?,
391 stamp_sec: reader.read_i32()?,
392 stamp_nanosec: reader.read_u32()?,
393 })
394 }
395}
396
397#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
401pub struct GoalStatusStamped {
402 pub goal_info: GoalInfo,
404 pub status: GoalStatus,
406}
407
408impl GoalStatusStamped {
409 pub const fn new(goal_info: GoalInfo, status: GoalStatus) -> Self {
411 Self { goal_info, status }
412 }
413}
414
415impl Serialize for GoalStatusStamped {
416 fn serialize(&self, writer: &mut CdrWriter) -> Result<(), SerError> {
417 self.goal_info.serialize(writer)?;
418 writer.write_i8(self.status as i8)?;
419 Ok(())
420 }
421}
422
423impl Deserialize for GoalStatusStamped {
424 fn deserialize(reader: &mut CdrReader) -> Result<Self, DeserError> {
425 let goal_info = GoalInfo::deserialize(reader)?;
426 let status_val = reader.read_i8()?;
427 let status = GoalStatus::from_i8(status_val).unwrap_or(GoalStatus::Unknown);
428 Ok(Self { goal_info, status })
429 }
430}
431
432#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
451#[repr(i8)]
452pub enum CancelReturnCode {
453 #[default]
455 Ok = 0,
456 Rejected = 1,
458 UnknownGoal = 2,
460 GoalTerminated = 3,
462}
463
464impl CancelReturnCode {
465 pub fn from_i8(value: i8) -> Option<Self> {
467 match value {
468 0 => Some(CancelReturnCode::Ok),
469 1 => Some(CancelReturnCode::Rejected),
470 2 => Some(CancelReturnCode::UnknownGoal),
471 3 => Some(CancelReturnCode::GoalTerminated),
472 _ => None,
473 }
474 }
475}
476
477impl Serialize for CancelReturnCode {
478 fn serialize(&self, writer: &mut CdrWriter) -> Result<(), SerError> {
479 writer.write_i8(*self as i8)
480 }
481}
482
483impl Deserialize for CancelReturnCode {
484 fn deserialize(reader: &mut CdrReader) -> Result<Self, DeserError> {
485 let value = reader.read_i8()?;
486 CancelReturnCode::from_i8(value).ok_or(DeserError::InvalidData)
487 }
488}
489
490#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
502#[repr(i8)]
503pub enum CancelResponse {
504 #[default]
506 Reject = 0,
507 Accept = 1,
509}
510
511impl CancelResponse {
512 pub fn from_i8(value: i8) -> Option<Self> {
514 match value {
515 0 => Some(CancelResponse::Reject),
516 1 => Some(CancelResponse::Accept),
517 _ => None,
518 }
519 }
520}
521
522#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
524#[repr(i8)]
525pub enum GoalResponse {
526 #[default]
528 Reject = 0,
529 AcceptAndExecute = 1,
531 AcceptAndDefer = 2,
533}
534
535impl GoalResponse {
536 pub fn from_i8(value: i8) -> Option<Self> {
538 match value {
539 0 => Some(GoalResponse::Reject),
540 1 => Some(GoalResponse::AcceptAndExecute),
541 2 => Some(GoalResponse::AcceptAndDefer),
542 _ => None,
543 }
544 }
545
546 pub fn is_accepted(&self) -> bool {
548 matches!(
549 self,
550 GoalResponse::AcceptAndExecute | GoalResponse::AcceptAndDefer
551 )
552 }
553}
554
555#[cfg(test)]
567mod tests {
568 extern crate std;
569
570 use super::*;
571 use std::format;
572
573 #[test]
574 fn test_goal_status_is_terminal() {
575 assert!(!GoalStatus::Unknown.is_terminal());
576 assert!(!GoalStatus::Accepted.is_terminal());
577 assert!(!GoalStatus::Executing.is_terminal());
578 assert!(!GoalStatus::Canceling.is_terminal());
579 assert!(GoalStatus::Succeeded.is_terminal());
580 assert!(GoalStatus::Canceled.is_terminal());
581 assert!(GoalStatus::Aborted.is_terminal());
582 }
583
584 #[test]
585 fn test_goal_status_is_active() {
586 assert!(!GoalStatus::Unknown.is_active());
587 assert!(GoalStatus::Accepted.is_active());
588 assert!(GoalStatus::Executing.is_active());
589 assert!(GoalStatus::Canceling.is_active());
590 assert!(!GoalStatus::Succeeded.is_active());
591 assert!(!GoalStatus::Canceled.is_active());
592 assert!(!GoalStatus::Aborted.is_active());
593 }
594
595 #[test]
596 fn test_goal_status_from_i8() {
597 assert_eq!(GoalStatus::from_i8(0), Some(GoalStatus::Unknown));
598 assert_eq!(GoalStatus::from_i8(1), Some(GoalStatus::Accepted));
599 assert_eq!(GoalStatus::from_i8(2), Some(GoalStatus::Executing));
600 assert_eq!(GoalStatus::from_i8(3), Some(GoalStatus::Canceling));
601 assert_eq!(GoalStatus::from_i8(4), Some(GoalStatus::Succeeded));
602 assert_eq!(GoalStatus::from_i8(5), Some(GoalStatus::Canceled));
603 assert_eq!(GoalStatus::from_i8(6), Some(GoalStatus::Aborted));
604 assert_eq!(GoalStatus::from_i8(7), None);
605 assert_eq!(GoalStatus::from_i8(-1), None);
606 }
607
608 #[test]
609 fn test_goal_id_zero() {
610 let id = GoalId::zero();
611 assert!(id.is_zero());
612 assert_eq!(id.uuid, [0; 16]);
613 }
614
615 #[test]
616 fn test_goal_id_from_counter() {
617 let id1 = GoalId::from_counter(1);
618 let id2 = GoalId::from_counter(2);
619
620 assert!(!id1.is_zero());
621 assert!(!id2.is_zero());
622 assert_ne!(id1, id2);
623
624 let id1_again = GoalId::from_counter(1);
626 assert_eq!(id1, id1_again);
627 }
628
629 #[test]
630 fn test_goal_id_display() {
631 let id = GoalId::new([
632 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66,
633 0x77, 0x88,
634 ]);
635 let s = format!("{}", id);
636 assert_eq!(s, "12345678-9abc-def0-1122-334455667788");
637 }
638
639 #[test]
640 fn test_cancel_return_code_from_i8() {
641 assert_eq!(CancelReturnCode::from_i8(0), Some(CancelReturnCode::Ok));
642 assert_eq!(
643 CancelReturnCode::from_i8(1),
644 Some(CancelReturnCode::Rejected)
645 );
646 assert_eq!(
647 CancelReturnCode::from_i8(2),
648 Some(CancelReturnCode::UnknownGoal)
649 );
650 assert_eq!(
651 CancelReturnCode::from_i8(3),
652 Some(CancelReturnCode::GoalTerminated)
653 );
654 assert_eq!(CancelReturnCode::from_i8(4), None);
655 }
656
657 #[test]
663 fn test_cancel_response_is_not_a_return_code() {
664 assert_eq!(CancelResponse::from_i8(0), Some(CancelResponse::Reject));
665 assert_eq!(CancelResponse::from_i8(1), Some(CancelResponse::Accept));
666 assert_eq!(CancelResponse::from_i8(2), None);
667 assert_eq!(CancelResponse::default(), CancelResponse::Reject);
668
669 assert_eq!(CancelResponse::Reject as i8, CancelReturnCode::Ok as i8);
671 assert_eq!(
672 CancelResponse::Accept as i8,
673 CancelReturnCode::Rejected as i8
674 );
675 }
676
677 #[test]
678 fn test_goal_response_is_accepted() {
679 assert!(!GoalResponse::Reject.is_accepted());
680 assert!(GoalResponse::AcceptAndExecute.is_accepted());
681 assert!(GoalResponse::AcceptAndDefer.is_accepted());
682 }
683
684 #[test]
685 fn test_goal_info_new() {
686 let goal_id = GoalId::from_counter(42);
687 let info = GoalInfo::new(goal_id, 123, 456);
688 assert_eq!(info.goal_id, goal_id);
689 assert_eq!(info.stamp_sec, 123);
690 assert_eq!(info.stamp_nanosec, 456);
691 }
692
693 #[test]
694 fn test_goal_info_with_id() {
695 let goal_id = GoalId::from_counter(42);
696 let info = GoalInfo::with_id(goal_id);
697 assert_eq!(info.goal_id, goal_id);
698 assert_eq!(info.stamp_sec, 0);
699 assert_eq!(info.stamp_nanosec, 0);
700 }
701
702 #[test]
703 fn test_goal_status_stamped() {
704 let goal_id = GoalId::from_counter(1);
705 let info = GoalInfo::with_id(goal_id);
706 let stamped = GoalStatusStamped::new(info, GoalStatus::Executing);
707 assert_eq!(stamped.goal_info.goal_id, goal_id);
708 assert_eq!(stamped.status, GoalStatus::Executing);
709 }
710
711 #[test]
712 fn test_goal_id_serialization() {
713 use nros_serdes::{CdrReader, CdrWriter};
714
715 let original = GoalId::new([
716 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
717 0x0f, 0x10,
718 ]);
719
720 let mut buf = [0u8; 32];
722 let mut writer = CdrWriter::new(&mut buf);
723 original.serialize(&mut writer).unwrap();
724 let len = writer.position();
725 assert_eq!(len, 16); let mut reader = CdrReader::new(&buf[..len]);
729 let deserialized = GoalId::deserialize(&mut reader).unwrap();
730
731 assert_eq!(original, deserialized);
732 }
733
734 #[test]
735 fn test_goal_status_serialization() {
736 use nros_serdes::{CdrReader, CdrWriter};
737
738 for status in [
739 GoalStatus::Unknown,
740 GoalStatus::Accepted,
741 GoalStatus::Executing,
742 GoalStatus::Canceling,
743 GoalStatus::Succeeded,
744 GoalStatus::Canceled,
745 GoalStatus::Aborted,
746 ] {
747 let mut buf = [0u8; 8];
748 let len = {
749 let mut writer = CdrWriter::new(&mut buf);
750 status.serialize(&mut writer).unwrap();
751 writer.position()
752 };
753
754 let mut reader = CdrReader::new(&buf[..len]);
755 let deserialized = GoalStatus::deserialize(&mut reader).unwrap();
756
757 assert_eq!(status, deserialized);
758 }
759 }
760
761 #[test]
762 fn test_goal_info_serialization() {
763 use nros_serdes::{CdrReader, CdrWriter};
764
765 let goal_id = GoalId::from_counter(42);
766 let original = GoalInfo::new(goal_id, 1234567890, 123456789);
767
768 let mut buf = [0u8; 64];
770 let mut writer = CdrWriter::new(&mut buf);
771 original.serialize(&mut writer).unwrap();
772 let len = writer.position();
773 assert_eq!(len, 24);
775
776 let mut reader = CdrReader::new(&buf[..len]);
778 let deserialized = GoalInfo::deserialize(&mut reader).unwrap();
779
780 assert_eq!(original.goal_id, deserialized.goal_id);
781 assert_eq!(original.stamp_sec, deserialized.stamp_sec);
782 assert_eq!(original.stamp_nanosec, deserialized.stamp_nanosec);
783 }
784
785 #[test]
786 fn test_goal_status_stamped_serialization() {
787 use nros_serdes::{CdrReader, CdrWriter};
788
789 let goal_id = GoalId::from_counter(99);
790 let goal_info = GoalInfo::new(goal_id, 987654321, 111222333);
791 let original = GoalStatusStamped::new(goal_info, GoalStatus::Succeeded);
792
793 let mut buf = [0u8; 64];
795 let mut writer = CdrWriter::new(&mut buf);
796 original.serialize(&mut writer).unwrap();
797 let len = writer.position();
798 assert_eq!(len, 25);
800
801 let mut reader = CdrReader::new(&buf[..len]);
803 let deserialized = GoalStatusStamped::deserialize(&mut reader).unwrap();
804
805 assert_eq!(original.goal_info.goal_id, deserialized.goal_info.goal_id);
806 assert_eq!(
807 original.goal_info.stamp_sec,
808 deserialized.goal_info.stamp_sec
809 );
810 assert_eq!(
811 original.goal_info.stamp_nanosec,
812 deserialized.goal_info.stamp_nanosec
813 );
814 assert_eq!(original.status, deserialized.status);
815 }
816}
817
818#[cfg(kani)]
823mod verification {
824 use super::*;
825
826 #[kani::proof]
829 fn goal_status_from_i8_valid_range() {
830 let val: i8 = kani::any();
831 let status = GoalStatus::from_i8(val);
832 if (0..=6).contains(&val) {
833 assert!(status.is_some());
834 } else {
835 assert!(status.is_none());
836 }
837 }
838
839 #[kani::proof]
840 fn goal_status_terminal_active_exclusive() {
841 let val: i8 = kani::any();
842 kani::assume((0..=6).contains(&val));
843 let status = GoalStatus::from_i8(val).unwrap();
844 assert!(!(status.is_terminal() && status.is_active()));
846 if val == 0 {
848 assert!(!status.is_terminal());
849 assert!(!status.is_active());
850 }
851 if (1..=3).contains(&val) {
853 assert!(status.is_active());
854 assert!(!status.is_terminal());
855 }
856 if (4..=6).contains(&val) {
858 assert!(status.is_terminal());
859 assert!(!status.is_active());
860 }
861 }
862
863 #[kani::proof]
864 #[kani::unwind(5)]
865 fn goal_status_serialize_roundtrip() {
866 let val: i8 = kani::any();
867 kani::assume((0..=6).contains(&val));
868 let status = GoalStatus::from_i8(val).unwrap();
869
870 let mut buf = [0u8; 8];
871 let len = {
872 let mut writer = CdrWriter::new(&mut buf);
873 status.serialize(&mut writer).unwrap();
874 writer.position()
875 };
876
877 let mut reader = CdrReader::new(&buf[..len]);
878 let deserialized = GoalStatus::deserialize(&mut reader).unwrap();
879 assert_eq!(status, deserialized);
880 }
881
882 #[kani::proof]
885 fn goal_response_from_i8_valid_range() {
886 let val: i8 = kani::any();
887 let resp = GoalResponse::from_i8(val);
888 if (0..=2).contains(&val) {
889 assert!(resp.is_some());
890 } else {
891 assert!(resp.is_none());
892 }
893 }
894
895 #[kani::proof]
896 fn goal_response_is_accepted_consistent() {
897 let val: i8 = kani::any();
898 kani::assume((0..=2).contains(&val));
899 let resp = GoalResponse::from_i8(val).unwrap();
900 assert_eq!(resp.is_accepted(), val >= 1);
902 }
903
904 #[kani::proof]
907 fn cancel_return_code_from_i8_valid_range() {
908 let val: i8 = kani::any();
909 let resp = CancelReturnCode::from_i8(val);
910 if (0..=3).contains(&val) {
911 assert!(resp.is_some());
912 } else {
913 assert!(resp.is_none());
914 }
915 }
916
917 #[kani::proof]
920 fn cancel_response_from_i8_valid_range() {
921 let val: i8 = kani::any();
922 let resp = CancelResponse::from_i8(val);
923 if (0..=1).contains(&val) {
924 assert!(resp.is_some());
925 } else {
926 assert!(resp.is_none());
927 }
928 }
929
930 #[kani::proof]
933 fn goal_id_zero_is_zero() {
934 let id = GoalId::zero();
935 assert!(id.is_zero());
936 }
937
938 #[kani::proof]
939 fn goal_id_from_counter_deterministic() {
940 let counter: u64 = kani::any();
941 kani::assume(counter <= 1_000_000);
943 let id1 = GoalId::from_counter(counter);
944 let id2 = GoalId::from_counter(counter);
945 assert_eq!(id1, id2);
946 }
947
948 #[kani::proof]
949 fn goal_id_from_counter_not_zero() {
950 let counter: u64 = kani::any();
951 kani::assume(counter > 0 && counter <= 1_000_000);
952 let id = GoalId::from_counter(counter);
953 assert!(!id.is_zero());
954 }
955
956 #[kani::proof]
957 #[kani::unwind(20)]
958 fn goal_id_serialize_roundtrip() {
959 let mut uuid = [0u8; 16];
960 uuid[0] = kani::any();
962 uuid[7] = kani::any();
963 uuid[15] = kani::any();
964 let id = GoalId::new(uuid);
965
966 let mut buf = [0u8; 32];
967 let len = {
968 let mut writer = CdrWriter::new(&mut buf);
969 id.serialize(&mut writer).unwrap();
970 writer.position()
971 };
972
973 let mut reader = CdrReader::new(&buf[..len]);
974 let deserialized = GoalId::deserialize(&mut reader).unwrap();
975 assert_eq!(id, deserialized);
976 }
977}