pamoja_mavlink/error.rs
1//! The error model for the MAVLink wire layer.
2//!
3//! A single [`MavlinkError`] keeps framing, checksum, and signing faults uniform, the
4//! way [`pamoja-modbus`](https://docs.rs/pamoja-modbus) and
5//! [`pamoja-lorawan`](https://docs.rs/pamoja-lorawan) each carry their own protocol
6//! error type ahead of the transport layer.
7
8use core::fmt;
9
10/// A fault encountered while building, parsing, signing, or verifying a frame.
11///
12/// This enum is `#[non_exhaustive]`: new variants may be added without a breaking
13/// change, so a downstream `match` must include a wildcard arm.
14#[derive(Clone, Copy, Debug, PartialEq, Eq)]
15#[non_exhaustive]
16pub enum MavlinkError {
17 /// The bytes are shorter than the smallest valid frame of their version.
18 FrameTooShort,
19
20 /// The first byte is neither the v1 (`0xFE`) nor the v2 (`0xFD`) start marker.
21 BadMagic(u8),
22
23 /// The buffer ended before the frame its length field promised was complete.
24 Truncated,
25
26 /// A v2 frame sets an incompatibility flag this parser does not understand, so its
27 /// layout cannot be trusted, and the specification requires the frame be dropped.
28 /// Carries the flags byte.
29 UnknownIncompatFlags(u8),
30
31 /// The trailing checksum did not match the one computed over the frame.
32 CrcMismatch {
33 /// The checksum computed over the received bytes.
34 expected: u16,
35 /// The checksum the frame carried.
36 found: u16,
37 },
38
39 /// A message id has no known `CRC_EXTRA`, so its checksum cannot be validated and
40 /// its payload cannot be decoded.
41 UnknownMessage(u32),
42
43 /// A payload, frame, or field array was larger than the protocol or buffer allows.
44 PayloadTooLong,
45
46 /// A signature was required but the frame was not signed.
47 Unsigned,
48
49 /// The frame's signature did not match the one computed with the key.
50 BadSignature,
51
52 /// The frame's signing timestamp was older than the link's replay window allows.
53 ReplayedTimestamp,
54
55 /// A typed message was decoded from a payload of the wrong size or shape.
56 BadPayload,
57
58 /// The message has no field of the requested name.
59 UnknownField,
60
61 /// A field type name is not one MAVLink defines.
62 UnknownFieldType,
63
64 /// A message shape names the same field twice.
65 DuplicateField,
66
67 /// A field was read or written as a kind its type is not.
68 FieldTypeMismatch,
69
70 /// An array element index is past the end of the field.
71 FieldIndexOutOfRange,
72
73 /// A value does not fit the field's type.
74 ValueOutOfRange,
75
76 /// The link reached end of input before a frame could be read.
77 Closed,
78}
79
80impl fmt::Display for MavlinkError {
81 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
82 match self {
83 Self::FrameTooShort => f.write_str("frame is shorter than a valid frame"),
84 Self::BadMagic(byte) => write!(f, "unrecognized start marker: {byte:#04x}"),
85 Self::Truncated => f.write_str("frame is shorter than its length field promises"),
86 Self::UnknownIncompatFlags(flags) => write!(
87 f,
88 "frame sets incompatibility flags {flags:#04x}, which this parser does not understand"
89 ),
90 Self::CrcMismatch { expected, found } => {
91 write!(
92 f,
93 "checksum mismatch: expected {expected:#06x}, found {found:#06x}"
94 )
95 }
96 Self::UnknownMessage(id) => write!(f, "no CRC_EXTRA known for message id {id}"),
97 Self::PayloadTooLong => f.write_str("payload exceeds the maximum frame size"),
98 Self::Unsigned => f.write_str("frame is not signed"),
99 Self::BadSignature => f.write_str("signature does not verify"),
100 Self::ReplayedTimestamp => f.write_str("signing timestamp is too old"),
101 Self::BadPayload => f.write_str("payload does not match the message layout"),
102 Self::UnknownField => f.write_str("message has no field of that name"),
103 Self::UnknownFieldType => f.write_str("unrecognized MAVLink field type"),
104 Self::DuplicateField => f.write_str("message names the same field twice"),
105 Self::FieldTypeMismatch => f.write_str("field is not of the requested kind"),
106 Self::FieldIndexOutOfRange => f.write_str("array index is past the end of the field"),
107 Self::ValueOutOfRange => f.write_str("value does not fit the field type"),
108 Self::Closed => f.write_str("link reached end of input"),
109 }
110 }
111}
112
113#[cfg(feature = "std")]
114impl std::error::Error for MavlinkError {}
115
116/// A specialized [`core::result::Result`] whose error type is [`MavlinkError`].
117pub type Result<T> = core::result::Result<T, MavlinkError>;