libgraphql_parser/graphql_parse_error_kind.rs
1use crate::DefinitionKind;
2use crate::DocumentKind;
3use crate::ReservedNameContext;
4use crate::ValueParsingError;
5
6/// Categorizes parse errors for programmatic handling.
7///
8/// Each variant contains minimal data needed for programmatic decisions.
9/// Human-readable context (suggestions, explanations) belongs in the
10/// `notes` field of `GraphQLParseError`.
11///
12/// The `#[error(...)]` messages are concise/programmatic. Full human-readable
13/// messages are in `GraphQLParseError.message`.
14#[derive(Debug, Clone, PartialEq, thiserror::Error)]
15pub enum GraphQLParseErrorKind {
16 /// Expected specific token(s) but found something else.
17 ///
18 /// This is the most common error type — the parser expected certain tokens
19 /// based on grammar rules but encountered something unexpected.
20 ///
21 /// # Example
22 /// ```text
23 /// type User { name String }
24 /// ^^^^^^ expected `:`, found `String`
25 /// ```
26 #[error("unexpected token: `{found}`")]
27 UnexpectedToken {
28 /// What tokens were expected (e.g., `[":", "{", "@"]`).
29 expected: Vec<String>,
30 /// Description of what was found (e.g., `"String"` or `"}"`).
31 found: String,
32 },
33
34 /// Unexpected end of input while parsing.
35 ///
36 /// The document ended before a complete construct was parsed.
37 ///
38 /// # Example
39 /// ```text
40 /// type User {
41 /// ^ expected `}`, found end of input
42 /// ```
43 #[error("unexpected end of input")]
44 UnexpectedEof {
45 /// What was expected when EOF was encountered.
46 expected: Vec<String>,
47 },
48
49 /// Lexer error encountered during parsing.
50 ///
51 /// The parser encountered a `GraphQLTokenKind::Error` token from the lexer.
52 /// The lexer's error message and notes are preserved in the parent
53 /// `GraphQLParseError`'s `message` and `notes` fields.
54 ///
55 /// # Example
56 /// ```text
57 /// type User { name: "unterminated string
58 /// ^ unterminated string literal
59 /// ```
60 #[error("lexer error")]
61 LexerError,
62
63 /// Unclosed delimiter (bracket, brace, or parenthesis).
64 ///
65 /// A delimiter was opened but EOF was reached before finding the matching
66 /// closing delimiter. The opening location is typically included in the
67 /// error's `notes`.
68 ///
69 /// # Example
70 /// ```text
71 /// type User {
72 /// name: String
73 /// # EOF here — missing `}`
74 /// ```
75 ///
76 /// Note: This is distinct from `MismatchedDelimiter`, which occurs when a
77 /// *wrong* closing delimiter is found (e.g., `[` closed with `)`).
78 #[error("unclosed delimiter: `{delimiter}`")]
79 UnclosedDelimiter {
80 /// The unclosed delimiter (e.g., `"{"`, `"["`, `"("`).
81 delimiter: String,
82 },
83
84 /// Mismatched delimiter.
85 ///
86 /// A closing delimiter was found that doesn't match the most recently
87 /// opened delimiter. This indicates a structural nesting error.
88 ///
89 /// # Example
90 /// ```text
91 /// type User { name: [String) }
92 /// ^ expected `]`, found `)`
93 /// ```
94 ///
95 /// Note: This is distinct from `UnclosedDelimiter`, which occurs when EOF
96 /// is reached without any closing delimiter.
97 #[error("mismatched delimiter")]
98 MismatchedDelimiter {
99 /// The expected closing delimiter (e.g., `"]"`).
100 expected: String,
101 /// The actual closing delimiter found (e.g., `")"`).
102 found: String,
103 },
104
105 /// Invalid value (wraps value parsing errors).
106 ///
107 /// Occurs when a literal value (string, int, float) cannot be parsed.
108 ///
109 /// # Example
110 /// ```text
111 /// query { field(limit: 99999999999999999999) }
112 /// ^^^^^^^^^^^^^^^^^^^^ integer overflow
113 /// ```
114 #[error("invalid value")]
115 InvalidValue(ValueParsingError),
116
117 /// Reserved name used in a context where it's not allowed.
118 ///
119 /// Certain names have special meaning in specific contexts:
120 /// - `on` cannot be a fragment name (it introduces type conditions)
121 /// - `true`, `false`, `null` cannot be enum values (ambiguous with literals)
122 ///
123 /// # Example
124 /// ```text
125 /// fragment on on User { name }
126 /// ^^ fragment name cannot be `on`
127 /// ```
128 #[error("reserved name: `{name}`")]
129 ReservedName {
130 /// The reserved name that was used (e.g., `"on"`, `"true"`).
131 name: String,
132 /// The context where this name is not allowed.
133 context: ReservedNameContext,
134 },
135
136 /// Definition kind not allowed in the document being parsed.
137 ///
138 /// When parsing with `parse_executable_document()`, schema definitions
139 /// (types, directives) are not allowed. When parsing with
140 /// `parse_schema_document()`, operations and fragments are not allowed.
141 ///
142 /// # Example
143 /// ```text
144 /// # Parsing as executable document:
145 /// type User { name: String }
146 /// ^^^^ type definition not allowed in executable document
147 /// ```
148 #[error("wrong document kind")]
149 WrongDocumentKind {
150 /// What kind of definition was found.
151 found: DefinitionKind,
152 /// What kind of document is being parsed.
153 document_kind: DocumentKind,
154 },
155
156 /// Empty construct that requires content.
157 ///
158 /// Certain constructs cannot be empty per the GraphQL spec:
159 /// - Selection sets: `{ }` is invalid (must have at least one selection)
160 /// - Argument lists: `()` is invalid (omit parentheses if no arguments)
161 ///
162 /// # Example
163 /// ```text
164 /// query { user { } }
165 /// ^^^ selection set cannot be empty
166 /// ```
167 #[error("invalid empty construct: `{construct}`")]
168 InvalidEmptyConstruct {
169 /// What construct is empty (e.g., `"selection set"`).
170 construct: String,
171 },
172
173 /// Invalid syntax that doesn't fit other categories.
174 ///
175 /// A catch-all for syntax errors without dedicated variants. The specific
176 /// error is described in `GraphQLParseError.message`.
177 #[error("invalid syntax")]
178 InvalidSyntax,
179}