orrery_parser/error/error_code.rs
1//! Error codes for the Orrery diagnostic system.
2//!
3//! Error codes are organized by phase:
4//! - `E0xx` - Lexer errors
5//! - `E1xx` - Parser errors
6//! - `E2xx` - Validation errors
7//! - `E3xx` - Elaboration errors
8//! - `E4xx` - Resolver errors
9
10use std::fmt;
11
12/// Error codes for categorizing diagnostic errors.
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
14pub enum ErrorCode {
15 // =========================================================================
16 // Lexer Errors (E0xx)
17 // =========================================================================
18 /// Unterminated string literal.
19 ///
20 /// A string was opened with a quote but never closed.
21 E001,
22
23 /// Unexpected character.
24 ///
25 /// A character was encountered that is not valid in this context.
26 E002,
27
28 /// Invalid escape sequence.
29 ///
30 /// An unrecognized escape sequence was used in a string literal.
31 /// Valid escapes are: `\n`, `\r`, `\t`, `\b`, `\f`, `\\`, `\/`, `\'`, `\"`, `\0`, `\u{...}`.
32 E003,
33
34 /// Invalid unicode escape format.
35 ///
36 /// A unicode escape sequence was malformed. Unicode escapes must use
37 /// the format `\u{XXXX}` with 1-6 hexadecimal digits.
38 E004,
39
40 /// Invalid unicode codepoint.
41 ///
42 /// The unicode codepoint is out of range or in the surrogate range.
43 /// Valid codepoints are 0x0000-0xD7FF and 0xE000-0x10FFFF.
44 E005,
45
46 /// Empty unicode escape.
47 ///
48 /// A unicode escape `\u{}` was found with no hexadecimal digits.
49 E006,
50
51 // =========================================================================
52 // Parser Errors (E1xx)
53 // =========================================================================
54 /// Unexpected token.
55 ///
56 /// The parser encountered a token it did not expect at this position.
57 E100,
58
59 /// Incomplete input.
60 ///
61 /// The input ended unexpectedly before a complete construct was parsed.
62 E101,
63
64 // =========================================================================
65 // Validation Errors (E2xx)
66 // =========================================================================
67 /// Undefined component reference.
68 ///
69 /// A component was referenced that has not been defined.
70 E200,
71
72 /// Unpaired activate statement.
73 ///
74 /// An `activate` statement has no matching `deactivate`.
75 E201,
76
77 /// Unpaired deactivate statement.
78 ///
79 /// A `deactivate` statement has no matching `activate`.
80 E202,
81
82 /// Invalid align value for diagram type.
83 ///
84 /// The specified alignment is not valid for this diagram type.
85 E203,
86
87 /// Unknown embed reference.
88 ///
89 /// An `embed <name>` references an identifier that doesn't match any
90 /// namespaced import in the current file.
91 E204,
92
93 // =========================================================================
94 // Elaboration Errors (E3xx)
95 // =========================================================================
96 /// Undefined type reference.
97 ///
98 /// A type was referenced that has not been defined.
99 E300,
100
101 /// Type override not supported.
102 ///
103 /// Overriding is not supported for this type.
104 E301,
105
106 /// Invalid attribute value.
107 ///
108 /// An attribute value is not valid for the expected type.
109 E302,
110
111 /// Unknown attribute.
112 ///
113 /// An attribute was specified that is not recognized.
114 E303,
115
116 /// Unsupported attribute for shape/type.
117 ///
118 /// The attribute is valid but not supported for this particular shape or type.
119 E304,
120
121 /// Nested diagram not allowed.
122 ///
123 /// A diagram definition was found inside another diagram.
124 E305,
125
126 /// Invalid diagram structure.
127 ///
128 /// The diagram structure is invalid or malformed.
129 E306,
130
131 /// Type mismatch.
132 ///
133 /// A type was used in a context where a different kind of type was expected
134 /// (e.g., using an Arrow type where a Shape type is required).
135 E307,
136
137 /// Shape does not support nested content.
138 ///
139 /// An attempt was made to add nested content to a shape type that
140 /// does not support it.
141 E308,
142
143 /// Unresolved embed reference.
144 ///
145 /// An `embed <name>` references a diagram that could not be resolved.
146 E309,
147
148 // =========================================================================
149 // Resolver Errors (E4xx)
150 // =========================================================================
151 /// File not found.
152 ///
153 /// A source file referenced by an import declaration or passed as the
154 /// root entry point could not be resolved or read.
155 E400,
156
157 /// Circular dependency.
158 ///
159 /// A file appears in its own import chain, forming a cycle.
160 E401,
161
162 /// Invalid import path.
163 ///
164 /// An import path is malformed (e.g., empty).
165 E402,
166
167 /// Invalid namespace derivation.
168 ///
169 /// A namespace identifier could not be derived from the import path
170 /// (e.g., the path has no file stem or contains non-UTF-8 components).
171 E403,
172}
173
174impl ErrorCode {
175 /// Returns the numeric code as a string (e.g., "E001").
176 pub fn as_str(&self) -> &'static str {
177 match self {
178 // Lexer errors
179 ErrorCode::E001 => "E001",
180 ErrorCode::E002 => "E002",
181 ErrorCode::E003 => "E003",
182 ErrorCode::E004 => "E004",
183 ErrorCode::E005 => "E005",
184 ErrorCode::E006 => "E006",
185 // Parser errors
186 ErrorCode::E100 => "E100",
187 ErrorCode::E101 => "E101",
188 // Validation errors
189 ErrorCode::E200 => "E200",
190 ErrorCode::E201 => "E201",
191 ErrorCode::E202 => "E202",
192 ErrorCode::E203 => "E203",
193 ErrorCode::E204 => "E204",
194 // Elaboration errors
195 ErrorCode::E300 => "E300",
196 ErrorCode::E301 => "E301",
197 ErrorCode::E302 => "E302",
198 ErrorCode::E303 => "E303",
199 ErrorCode::E304 => "E304",
200 ErrorCode::E305 => "E305",
201 ErrorCode::E306 => "E306",
202 ErrorCode::E307 => "E307",
203 ErrorCode::E308 => "E308",
204 ErrorCode::E309 => "E309",
205 // Resolver errors
206 ErrorCode::E400 => "E400",
207 ErrorCode::E401 => "E401",
208 ErrorCode::E402 => "E402",
209 ErrorCode::E403 => "E403",
210 }
211 }
212
213 /// Returns a short description of what this error code means.
214 pub fn description(&self) -> &'static str {
215 match self {
216 // Lexer errors
217 ErrorCode::E001 => "unterminated string literal",
218 ErrorCode::E002 => "unexpected character",
219 ErrorCode::E003 => "invalid escape sequence",
220 ErrorCode::E004 => "invalid unicode escape",
221 ErrorCode::E005 => "invalid unicode codepoint",
222 ErrorCode::E006 => "empty unicode escape",
223 // Parser errors
224 ErrorCode::E100 => "unexpected token",
225 ErrorCode::E101 => "incomplete input",
226 // Validation errors
227 ErrorCode::E200 => "undefined component",
228 ErrorCode::E201 => "unpaired activate",
229 ErrorCode::E202 => "unpaired deactivate",
230 ErrorCode::E203 => "invalid align value",
231 ErrorCode::E204 => "unknown embed reference",
232 // Elaboration errors
233 ErrorCode::E300 => "undefined type",
234 ErrorCode::E301 => "type override not supported",
235 ErrorCode::E302 => "invalid attribute value",
236 ErrorCode::E303 => "unknown attribute",
237 ErrorCode::E304 => "unsupported attribute",
238 ErrorCode::E305 => "nested diagram not allowed",
239 ErrorCode::E306 => "invalid diagram structure",
240 ErrorCode::E307 => "type mismatch",
241 ErrorCode::E308 => "shape does not support nested content",
242 ErrorCode::E309 => "unresolved embed reference",
243 // Resolver errors
244 ErrorCode::E400 => "file not found",
245 ErrorCode::E401 => "circular dependency",
246 ErrorCode::E402 => "invalid import path",
247 ErrorCode::E403 => "invalid namespace",
248 }
249 }
250}
251
252impl fmt::Display for ErrorCode {
253 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
254 write!(f, "{}", self.as_str())
255 }
256}
257
258#[cfg(test)]
259mod tests {
260 use super::*;
261
262 #[test]
263 fn test_error_code_display() {
264 assert_eq!(ErrorCode::E001.to_string(), "E001");
265 assert_eq!(ErrorCode::E100.to_string(), "E100");
266 assert_eq!(ErrorCode::E200.to_string(), "E200");
267 assert_eq!(ErrorCode::E300.to_string(), "E300");
268 assert_eq!(ErrorCode::E400.to_string(), "E400");
269 }
270
271 #[test]
272 fn test_error_code_as_str() {
273 assert_eq!(ErrorCode::E001.as_str(), "E001");
274 assert_eq!(ErrorCode::E306.as_str(), "E306");
275 }
276
277 #[test]
278 fn test_error_code_description() {
279 assert_eq!(ErrorCode::E001.description(), "unterminated string literal");
280 assert_eq!(ErrorCode::E200.description(), "undefined component");
281 assert_eq!(ErrorCode::E301.description(), "type override not supported");
282 assert_eq!(ErrorCode::E400.description(), "file not found");
283 }
284}