1use std::fmt;
21use thiserror::Error;
22
23#[derive(Debug, Clone, PartialEq, Eq)]
25pub enum HedlErrorKind {
26 Syntax,
28 Version,
30 Schema,
32 Alias,
34 Shape,
36 Semantic,
38 OrphanRow,
40 Collision,
42 Reference,
44 Security,
46 Conversion,
48 IO,
50}
51
52impl fmt::Display for HedlErrorKind {
53 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
54 match self {
55 Self::Syntax => write!(f, "SyntaxError"),
56 Self::Version => write!(f, "VersionError"),
57 Self::Schema => write!(f, "SchemaError"),
58 Self::Alias => write!(f, "AliasError"),
59 Self::Shape => write!(f, "ShapeError"),
60 Self::Semantic => write!(f, "SemanticError"),
61 Self::OrphanRow => write!(f, "OrphanRowError"),
62 Self::Collision => write!(f, "CollisionError"),
63 Self::Reference => write!(f, "ReferenceError"),
64 Self::Security => write!(f, "SecurityError"),
65 Self::Conversion => write!(f, "ConversionError"),
66 Self::IO => write!(f, "IOError"),
67 }
68 }
69}
70
71#[derive(Debug, Clone, Error)]
73#[error("{kind} at line {line}: {message}")]
74pub struct HedlError {
75 pub kind: HedlErrorKind,
77 pub message: String,
79 pub line: usize,
81 pub column: Option<usize>,
83 pub context: Option<String>,
85}
86
87impl HedlError {
88 pub fn new(kind: HedlErrorKind, message: impl Into<String>, line: usize) -> Self {
90 Self {
91 kind,
92 message: message.into(),
93 line,
94 column: None,
95 context: None,
96 }
97 }
98
99 pub fn with_line(mut self, line: usize) -> Self {
101 self.line = line;
102 self
103 }
104
105 pub fn with_column(mut self, column: usize) -> Self {
107 self.column = Some(column);
108 self
109 }
110
111 pub fn with_context(mut self, context: impl Into<String>) -> Self {
113 self.context = Some(context.into());
114 self
115 }
116
117 pub fn syntax(message: impl Into<String>, line: usize) -> Self {
121 Self::new(HedlErrorKind::Syntax, message, line)
122 }
123
124 pub fn version(message: impl Into<String>, line: usize) -> Self {
126 Self::new(HedlErrorKind::Version, message, line)
127 }
128
129 pub fn schema(message: impl Into<String>, line: usize) -> Self {
131 Self::new(HedlErrorKind::Schema, message, line)
132 }
133
134 pub fn alias(message: impl Into<String>, line: usize) -> Self {
136 Self::new(HedlErrorKind::Alias, message, line)
137 }
138
139 pub fn shape(message: impl Into<String>, line: usize) -> Self {
141 Self::new(HedlErrorKind::Shape, message, line)
142 }
143
144 pub fn semantic(message: impl Into<String>, line: usize) -> Self {
146 Self::new(HedlErrorKind::Semantic, message, line)
147 }
148
149 pub fn orphan_row(message: impl Into<String>, line: usize) -> Self {
151 Self::new(HedlErrorKind::OrphanRow, message, line)
152 }
153
154 pub fn collision(message: impl Into<String>, line: usize) -> Self {
156 Self::new(HedlErrorKind::Collision, message, line)
157 }
158
159 pub fn reference(message: impl Into<String>, line: usize) -> Self {
161 Self::new(HedlErrorKind::Reference, message, line)
162 }
163
164 pub fn security(message: impl Into<String>, line: usize) -> Self {
166 Self::new(HedlErrorKind::Security, message, line)
167 }
168
169 pub fn conversion(message: impl Into<String>) -> Self {
171 Self::new(HedlErrorKind::Conversion, message, 0)
172 }
173
174 pub fn io(message: impl Into<String>) -> Self {
176 Self::new(HedlErrorKind::IO, message, 0)
177 }
178
179 pub fn type_mismatch(message: impl Into<String>, line: usize) -> Self {
181 Self::new(HedlErrorKind::Semantic, message, line)
182 }
183}
184
185pub type HedlResult<T> = Result<T, HedlError>;
187
188#[cfg(test)]
189mod tests {
190 use super::*;
191
192 #[test]
195 fn test_error_kind_display_syntax() {
196 assert_eq!(format!("{}", HedlErrorKind::Syntax), "SyntaxError");
197 }
198
199 #[test]
200 fn test_error_kind_display_version() {
201 assert_eq!(format!("{}", HedlErrorKind::Version), "VersionError");
202 }
203
204 #[test]
205 fn test_error_kind_display_schema() {
206 assert_eq!(format!("{}", HedlErrorKind::Schema), "SchemaError");
207 }
208
209 #[test]
210 fn test_error_kind_display_alias() {
211 assert_eq!(format!("{}", HedlErrorKind::Alias), "AliasError");
212 }
213
214 #[test]
215 fn test_error_kind_display_shape() {
216 assert_eq!(format!("{}", HedlErrorKind::Shape), "ShapeError");
217 }
218
219 #[test]
220 fn test_error_kind_display_semantic() {
221 assert_eq!(format!("{}", HedlErrorKind::Semantic), "SemanticError");
222 }
223
224 #[test]
225 fn test_error_kind_display_orphan_row() {
226 assert_eq!(format!("{}", HedlErrorKind::OrphanRow), "OrphanRowError");
227 }
228
229 #[test]
230 fn test_error_kind_display_collision() {
231 assert_eq!(format!("{}", HedlErrorKind::Collision), "CollisionError");
232 }
233
234 #[test]
235 fn test_error_kind_display_reference() {
236 assert_eq!(format!("{}", HedlErrorKind::Reference), "ReferenceError");
237 }
238
239 #[test]
240 fn test_error_kind_display_security() {
241 assert_eq!(format!("{}", HedlErrorKind::Security), "SecurityError");
242 }
243
244 #[test]
247 fn test_error_kind_equality() {
248 assert_eq!(HedlErrorKind::Syntax, HedlErrorKind::Syntax);
249 assert_ne!(HedlErrorKind::Syntax, HedlErrorKind::Schema);
250 }
251
252 #[test]
253 fn test_error_kind_clone() {
254 let kind = HedlErrorKind::Reference;
255 let cloned = kind.clone();
256 assert_eq!(kind, cloned);
257 }
258
259 #[test]
262 fn test_error_display() {
263 let err = HedlError::new(HedlErrorKind::Syntax, "unexpected token", 42);
264 let msg = format!("{}", err);
265 assert!(msg.contains("SyntaxError"));
266 assert!(msg.contains("line 42"));
267 assert!(msg.contains("unexpected token"));
268 }
269
270 #[test]
271 fn test_error_with_column() {
272 let err = HedlError::syntax("error", 5).with_column(10);
273 assert_eq!(err.column, Some(10));
274 }
275
276 #[test]
277 fn test_error_with_context() {
278 let err = HedlError::syntax("error", 5).with_context("in struct User");
279 assert_eq!(err.context, Some("in struct User".to_string()));
280 }
281
282 #[test]
285 fn test_error_syntax() {
286 let err = HedlError::syntax("test", 1);
287 assert_eq!(err.kind, HedlErrorKind::Syntax);
288 assert_eq!(err.line, 1);
289 }
290
291 #[test]
292 fn test_error_version() {
293 let err = HedlError::version("test", 2);
294 assert_eq!(err.kind, HedlErrorKind::Version);
295 }
296
297 #[test]
298 fn test_error_schema() {
299 let err = HedlError::schema("test", 3);
300 assert_eq!(err.kind, HedlErrorKind::Schema);
301 }
302
303 #[test]
304 fn test_error_alias() {
305 let err = HedlError::alias("test", 4);
306 assert_eq!(err.kind, HedlErrorKind::Alias);
307 }
308
309 #[test]
310 fn test_error_shape() {
311 let err = HedlError::shape("test", 5);
312 assert_eq!(err.kind, HedlErrorKind::Shape);
313 }
314
315 #[test]
316 fn test_error_semantic() {
317 let err = HedlError::semantic("test", 6);
318 assert_eq!(err.kind, HedlErrorKind::Semantic);
319 }
320
321 #[test]
322 fn test_error_orphan_row() {
323 let err = HedlError::orphan_row("test", 7);
324 assert_eq!(err.kind, HedlErrorKind::OrphanRow);
325 }
326
327 #[test]
328 fn test_error_collision() {
329 let err = HedlError::collision("test", 8);
330 assert_eq!(err.kind, HedlErrorKind::Collision);
331 }
332
333 #[test]
334 fn test_error_reference() {
335 let err = HedlError::reference("test", 9);
336 assert_eq!(err.kind, HedlErrorKind::Reference);
337 }
338
339 #[test]
340 fn test_error_security() {
341 let err = HedlError::security("test", 10);
342 assert_eq!(err.kind, HedlErrorKind::Security);
343 }
344
345 #[test]
346 fn test_error_conversion() {
347 let err = HedlError::conversion("JSON serialization failed");
348 assert_eq!(err.kind, HedlErrorKind::Conversion);
349 assert_eq!(err.line, 0);
350 }
351
352 #[test]
353 fn test_error_io() {
354 let err = HedlError::io("Failed to read file");
355 assert_eq!(err.kind, HedlErrorKind::IO);
356 assert_eq!(err.line, 0);
357 }
358
359 #[test]
360 fn test_error_kind_display_conversion() {
361 assert_eq!(format!("{}", HedlErrorKind::Conversion), "ConversionError");
362 }
363
364 #[test]
365 fn test_error_kind_display_io() {
366 assert_eq!(format!("{}", HedlErrorKind::IO), "IOError");
367 }
368
369 #[test]
372 fn test_error_is_std_error() {
373 fn accepts_error<E: std::error::Error>(_: E) {}
374 accepts_error(HedlError::syntax("test", 1));
375 }
376
377 #[test]
378 fn test_error_clone() {
379 let original = HedlError::syntax("message", 5).with_column(10);
380 let cloned = original.clone();
381 assert_eq!(original.kind, cloned.kind);
382 assert_eq!(original.message, cloned.message);
383 assert_eq!(original.line, cloned.line);
384 assert_eq!(original.column, cloned.column);
385 }
386
387 #[test]
390 fn test_error_with_empty_message() {
391 let err = HedlError::syntax("", 1);
392 assert_eq!(err.message, "");
393 }
394
395 #[test]
396 fn test_error_with_unicode_message() {
397 let err = HedlError::syntax("日本語エラー 🎉", 1);
398 assert!(err.message.contains("🎉"));
399 }
400
401 #[test]
402 fn test_error_line_zero() {
403 let err = HedlError::syntax("test", 0);
405 assert_eq!(err.line, 0);
406 }
407
408 #[test]
409 fn test_error_large_line() {
410 let err = HedlError::syntax("test", usize::MAX);
411 assert_eq!(err.line, usize::MAX);
412 }
413
414 #[test]
415 fn test_error_chained_builders() {
416 let err = HedlError::syntax("error", 5)
417 .with_column(10)
418 .with_context("in list");
419 assert_eq!(err.column, Some(10));
420 assert_eq!(err.context, Some("in list".to_string()));
421 }
422
423 #[test]
424 fn test_error_debug() {
425 let err = HedlError::syntax("test", 1);
426 let debug = format!("{:?}", err);
427 assert!(debug.contains("Syntax"));
428 assert!(debug.contains("test"));
429 }
430}