Skip to main content

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}