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