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}