Skip to main content

nmea_kit/
error.rs

1/// Errors from frame-level parsing (checksum, delimiters, tag blocks).
2#[non_exhaustive]
3#[derive(Debug, Clone, PartialEq, Eq)]
4pub enum FrameError {
5    /// Input is empty or whitespace-only.
6    Empty,
7    /// First character is not `$` or `!`.
8    InvalidPrefix(char),
9    /// Checksum field is not valid hexadecimal.
10    MalformedChecksum,
11    /// Checksum mismatch.
12    BadChecksum { expected: u8, computed: u8 },
13    /// Tag block opened with `\` but not properly closed.
14    MalformedTagBlock,
15    /// Tag block checksum mismatch.
16    BadTagChecksum { expected: u8, computed: u8 },
17    /// Sentence too short to contain a valid address (minimum 3 chars, 4 for
18    /// proprietary `P` addresses).
19    TooShort,
20    /// Address (talker + type) contains non-ASCII bytes (NMEA addresses are ASCII).
21    NonAsciiAddress,
22}
23
24impl core::fmt::Display for FrameError {
25    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
26        match self {
27            Self::Empty => write!(f, "empty input"),
28            Self::InvalidPrefix(c) => write!(f, "invalid prefix '{c}', expected '$' or '!'"),
29            Self::MalformedChecksum => write!(f, "checksum is not valid hexadecimal"),
30            Self::BadChecksum { expected, computed } => {
31                write!(
32                    f,
33                    "checksum mismatch: expected {expected:02X}, computed {computed:02X}"
34                )
35            }
36            Self::MalformedTagBlock => write!(f, "malformed IEC 61162-450 tag block"),
37            Self::BadTagChecksum { expected, computed } => {
38                write!(
39                    f,
40                    "tag block checksum mismatch: expected {expected:02X}, computed {computed:02X}"
41                )
42            }
43            Self::TooShort => write!(f, "sentence too short"),
44            Self::NonAsciiAddress => write!(f, "address field contains non-ASCII bytes"),
45        }
46    }
47}
48
49impl std::error::Error for FrameError {}
50
51/// Errors from encoding (invalid frame parts or field values).
52#[non_exhaustive]
53#[derive(Debug, Clone, PartialEq, Eq)]
54pub enum EncodeError {
55    /// Prefix is not `$` or `!`.
56    InvalidPrefix(char),
57    /// Talker or sentence type contains non-ASCII characters.
58    NonAsciiAddress,
59    /// Sentence type is empty.
60    EmptySentenceType,
61    /// A field contains `,`, `*`, `\r`, `\n`, or a non-ASCII character.
62    InvalidFieldCharacter(char),
63    /// Coordinate magnitude is NaN, infinite, or negative.
64    InvalidCoordinate,
65    /// An AIS field is outside the range or format permitted by its bit layout.
66    InvalidAisField(&'static str),
67    /// AIS text exceeds the fixed-width field that carries it.
68    AisTextTooLong {
69        field: &'static str,
70        max_chars: usize,
71        actual_chars: usize,
72    },
73    /// A multi-fragment AIS payload requires a sequential message ID.
74    MissingAisSequenceId,
75    /// An AIS payload exceeds the five-fragment transmission limit.
76    TooManyAisFragments,
77}
78
79impl core::fmt::Display for EncodeError {
80    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
81        match self {
82            Self::InvalidPrefix(c) => write!(f, "invalid prefix '{c}', expected '$' or '!'"),
83            Self::NonAsciiAddress => write!(f, "talker or sentence type is not ASCII"),
84            Self::EmptySentenceType => write!(f, "sentence type is empty"),
85            Self::InvalidFieldCharacter(c) => {
86                write!(f, "field contains invalid character {c:?}")
87            }
88            Self::InvalidCoordinate => {
89                write!(f, "coordinate magnitude is NaN, infinite, or negative")
90            }
91            Self::InvalidAisField(field) => write!(f, "invalid AIS field {field}"),
92            Self::AisTextTooLong {
93                field,
94                max_chars,
95                actual_chars,
96            } => write!(
97                f,
98                "AIS field {field} is too long: {actual_chars} characters, maximum {max_chars}"
99            ),
100            Self::MissingAisSequenceId => {
101                write!(
102                    f,
103                    "multi-fragment AIS payload requires a sequential message ID"
104                )
105            }
106            Self::TooManyAisFragments => {
107                write!(
108                    f,
109                    "AIS payload exceeds the five-fragment transmission limit"
110                )
111            }
112        }
113    }
114}
115
116impl std::error::Error for EncodeError {}