Skip to main content

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>;