Skip to main content

rlsp_yaml_parser/
error.rs

1// SPDX-License-Identifier: MIT
2
3use crate::pos::Pos;
4
5/// The category of a parse error.
6///
7/// Consumers can match on `kind` to route errors without substring-matching
8/// `message` text.  `message` remains the authoritative human-readable description.
9#[derive(Debug, Clone, PartialEq, Eq)]
10#[non_exhaustive]
11pub enum ErrorKind {
12    /// A character that is not allowed in the current YAML context was found.
13    ///
14    /// Produced wherever the parser rejects a non-printable or otherwise
15    /// forbidden codepoint — e.g. `U+0001` in a comment body, a `\x07` hex
16    /// escape in a double-quoted scalar, or a NUL in a directive parameter.
17    InvalidCharacter,
18    /// A grammar or structural error that is not caused by a specific forbidden
19    /// character.
20    ///
21    /// Produced for unterminated scalars, bad indentation, duplicate directives,
22    /// and all other parse failures.
23    Syntax,
24}
25
26/// A parse error produced by the streaming parser.
27#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
28#[error("parse error at {pos:?}: {message}")]
29#[non_exhaustive]
30pub struct Error {
31    /// Source position where the parse error was detected.
32    pub pos: Pos,
33    /// Human-readable description of the error.
34    pub message: String,
35    /// Broad category of the error, for routing without message-string matching.
36    pub kind: ErrorKind,
37}
38
39impl Error {
40    /// Construct a [`ErrorKind::Syntax`] error.
41    #[must_use]
42    pub const fn syntax(pos: Pos, message: String) -> Self {
43        Self {
44            pos,
45            message,
46            kind: ErrorKind::Syntax,
47        }
48    }
49
50    /// Construct a [`ErrorKind::InvalidCharacter`] error.
51    #[must_use]
52    pub const fn invalid_character(pos: Pos, message: String) -> Self {
53        Self {
54            pos,
55            message,
56            kind: ErrorKind::InvalidCharacter,
57        }
58    }
59}
60
61#[cfg(test)]
62mod tests {
63    use super::*;
64
65    fn zero_pos() -> Pos {
66        Pos {
67            byte_offset: 0,
68            line: 1,
69            column: 0,
70        }
71    }
72
73    #[test]
74    fn error_syntax_constructor_sets_kind_syntax() {
75        let pos = zero_pos();
76        let err = Error::syntax(pos, "msg".to_owned());
77        assert_eq!(err.kind, ErrorKind::Syntax);
78        assert_eq!(err.message, "msg");
79        assert_eq!(err.pos, pos);
80    }
81
82    #[test]
83    fn error_invalid_character_constructor_sets_kind_invalid_character() {
84        let pos = zero_pos();
85        let err = Error::invalid_character(pos, "msg".to_owned());
86        assert_eq!(err.kind, ErrorKind::InvalidCharacter);
87        assert_eq!(err.message, "msg");
88        assert_eq!(err.pos, pos);
89    }
90}