vectorscan/error.rs
1/* Copyright 2022-2024 Danny McClanahan */
2/* SPDX-License-Identifier: BSD-3-Clause */
3
4//! Errors returned by methods in this library.
5
6use crate::hs;
7#[cfg(feature = "compiler")]
8use crate::matchers::ExpressionIndex;
9
10use displaydoc::Display;
11use thiserror::Error;
12
13#[cfg(feature = "compiler")]
14use std::{
15 ffi::{CStr, NulError},
16 fmt,
17 os::raw::c_uint,
18};
19
20/// Native error code from the underlying vectorscan library.
21#[derive(
22 Debug,
23 Display,
24 Error,
25 Copy,
26 Clone,
27 PartialEq,
28 Eq,
29 PartialOrd,
30 Ord,
31 Hash,
32 num_enum::IntoPrimitive,
33 num_enum::FromPrimitive,
34)]
35#[repr(i8)]
36#[ignore_extra_doc_attributes]
37pub enum VectorscanRuntimeError {
38 /// A parameter passed to this function was invalid.
39 ///
40 /// This error is only returned in cases where the function can detect an
41 /// invalid parameter -- it cannot be relied upon to detect (for example)
42 /// pointers to freed memory or other invalid data.
43 Invalid = hs::HS_INVALID,
44 /// A memory allocation failed.
45 NoMem = hs::HS_NOMEM,
46 /// The engine was terminated by callback.
47 ///
48 /// This return value indicates that the target buffer was partially scanned,
49 /// but that the callback function returned
50 /// [`MatchResult::CeaseMatching`](crate::matchers::MatchResult::CeaseMatching)
51 /// after a match was located.
52 ScanTerminated = hs::HS_SCAN_TERMINATED,
53 /// The pattern compiler failed, and the [`CompileError`] should be
54 /// inspected for more detail.
55 CompilerError = hs::HS_COMPILER_ERROR,
56 /// The given database was built for a different version of Vectorscan.
57 DbVersionError = hs::HS_DB_VERSION_ERROR,
58 /// The given database was built for a different platform (i.e., CPU type).
59 DbPlatformError = hs::HS_DB_PLATFORM_ERROR,
60 /// The given database was built for a different mode of operation.
61 ///
62 /// This error is returned when streaming calls are used with a block or
63 /// vectored database and vice versa.
64 DbModeError = hs::HS_DB_MODE_ERROR,
65 /// A parameter passed to this function was not correctly aligned.
66 BadAlign = hs::HS_BAD_ALIGN,
67 /// The memory allocator returned incorrectly aligned memory.
68 ///
69 /// The memory allocator (either [`libc::malloc()`] or the allocator set with
70 /// [`crate::alloc::set_allocator()`]) did not
71 /// correctly return memory suitably aligned for the largest representable
72 /// data type on this platform.
73 BadAlloc = hs::HS_BAD_ALLOC,
74 /// The scratch region was already in use.
75 ///
76 /// This error is returned when Vectorscan is able to detect that the scratch
77 /// region given is already in use by another Vectorscan API call.
78 ///
79 /// A separate scratch region, allocated with
80 /// [`Scratch::setup_for_db()`](crate::state::Scratch::setup_for_db) or
81 /// [`Scratch::try_clone()`](crate::state::Scratch::try_clone), is required
82 /// for every concurrent caller of the Vectorscan API.
83 ///
84 /// For example, this error might be returned when
85 /// [`scan_sync()`](crate::state::Scratch::scan_sync) has been
86 /// called inside a callback delivered by a currently-executing
87 /// [`scan_sync()`](crate::state::Scratch::scan_sync) call using the same
88 /// scratch region.
89 ///
90 /// Note: Not all concurrent uses of scratch regions may be detected. This
91 /// error is intended as a best-effort debugging tool, not a guarantee.
92 ///
93 /// Note: safe Rust code should never see this error. See [`crate::state`] for
94 /// ways to manage scratch spaces.
95 ScratchInUse = hs::HS_SCRATCH_IN_USE,
96 /// Unsupported CPU architecture.
97 ///
98 /// This error is returned when Vectorscan is able to detect that the current
99 /// system does not support the required instruction set.
100 ///
101 /// At a minimum, Vectorscan requires Supplemental Streaming SIMD Extensions 3
102 /// (SSSE3).
103 ArchError = hs::HS_ARCH_ERROR,
104 /// Provided buffer was too small.
105 ///
106 /// This error indicates that there was insufficient space in the buffer. The
107 /// call should be repeated with a larger provided buffer.
108 ///
109 /// Note: in this situation, it is normal for the amount of space required to
110 /// be returned in the same manner as the used space would have been
111 /// returned if the call was successful.
112 ///
113 /// This value is referenced internally in
114 /// [`LiveStream::compress()`](crate::stream::LiveStream) when requesting the
115 /// amount of memory to allocate for a compressed stream. Users of this
116 /// library should never see this error when using the
117 /// [`CompressReserveBehavior`](crate::stream::CompressReserveBehavior)
118 /// interface.
119 InsufficientSpace = hs::HS_INSUFFICIENT_SPACE,
120 /// Unexpected internal error.
121 ///
122 /// This error indicates that there was unexpected matching behaviors. This
123 /// could be related to invalid usage of stream and scratch space or invalid
124 /// memory operations by users.
125 #[num_enum(default)]
126 UnknownError = hs::HS_UNKNOWN_ERROR,
127}
128
129impl VectorscanRuntimeError {
130 pub(crate) fn from_native(x: hs::hs_error_t) -> Result<(), Self> {
131 static_assertions::const_assert_eq!(0, hs::HS_SUCCESS);
132 if x == 0 {
133 Ok(())
134 } else {
135 let s: Self = (x as i8).into();
136 Err(s)
137 }
138 }
139
140 #[cfg(feature = "compiler")]
141 pub(crate) fn copy_from_native_compile_error(
142 x: hs::hs_error_t,
143 c: *mut hs::hs_compile_error,
144 ) -> Result<(), VectorscanCompileError> {
145 match Self::from_native(x) {
146 Ok(()) => Ok(()),
147 Err(Self::CompilerError) => {
148 let e = CompileError::copy_from_native(unsafe { &mut *c }).unwrap();
149 Err(VectorscanCompileError::Compile(e))
150 },
151 Err(e) => Err(e.into()),
152 }
153 }
154}
155
156/// Error details returned by the pattern compiler.
157///
158/// This is returned by the compile calls
159/// ([`Database::compile()`](crate::database::Database::compile) and
160/// [`Database::compile_multi()`](crate::database::Database::compile_multi)) on
161/// failure. The caller may inspect the values returned in this type to
162/// determine the cause of failure.
163#[cfg(feature = "compiler")]
164#[cfg_attr(docsrs, doc(cfg(feature = "compiler")))]
165#[derive(Debug, Error)]
166pub struct CompileError {
167 /// A human-readable error message describing the error.
168 ///
169 /// # Common Errors
170 /// Common errors generated during the compile process include:
171 ///
172 /// - *Invalid parameter:* An invalid argument was specified in the compile
173 /// call.
174 ///
175 /// - *Unrecognised flag:* An unrecognised value was passed in the flags
176 /// argument.
177 ///
178 /// - *Pattern matches empty buffer:* By default, Vectorscan only supports
179 /// patterns that will *always* consume at least one byte of input. Patterns
180 /// that do not have this property (such as `/(abc)?/`) will produce this
181 /// error unless the [`Flags::ALLOWEMPTY`](crate::flags::Flags::ALLOWEMPTY)
182 /// flag is supplied. Note that such patterns will produce a match for
183 /// *every* byte when scanned.
184 ///
185 /// - *Embedded anchors not supported:* Vectorscan only supports the use of
186 /// anchor meta-characters (such as `^` and `$`) in patterns where they
187 /// could *only* match at the start or end of a buffer. A pattern containing
188 /// an embedded anchor, such as `/abc^def/`, can never match, as there is no
189 /// way for `abc` to precede the start of the data stream.
190 ///
191 /// - *Bounded repeat is too large:* The pattern contains a repeated construct
192 /// with very large finite bounds.
193 ///
194 /// - *Unsupported component type:* An unsupported PCRE construct was used in
195 /// the pattern. Consider using [`chimera`](crate::expression::chimera) for
196 /// full PCRE support.
197 ///
198 /// - *Unable to generate bytecode:* This error indicates that Vectorscan was
199 /// unable to compile a pattern that is syntactically valid. The most common
200 /// cause is a pattern that is very long and complex or contains a large
201 /// repeated subpattern.
202 ///
203 /// - *Unable to allocate memory:* The library was unable to allocate
204 /// temporary storage used during compilation time.
205 ///
206 /// - *Allocator returned misaligned memory:* The memory allocator (either
207 /// [`libc::malloc()`] or the allocator set with
208 /// [`set_db_allocator()`](crate::alloc::set_db_allocator)) did not
209 /// correctly return memory suitably aligned for the largest representable
210 /// data type on this platform.
211 ///
212 /// - *Internal error:* An unexpected error occurred: if this error is
213 /// reported, please contact the Vectorscan team with a description of the
214 /// situation.
215 pub message: String,
216 /// The zero-based number of the expression that caused the error (if this
217 /// can be determined). For a database with a single expression, this value
218 /// will be `0`:
219 ///
220 ///```
221 /// # fn main() -> Result<(), vectorscan::error::VectorscanError> {
222 /// use vectorscan::{expression::*, error::*, matchers::*, flags::*};
223 ///
224 /// let expr: Expression = "as(df".parse()?;
225 /// let index = match expr.compile(Flags::default(), Mode::BLOCK) {
226 /// Err(VectorscanCompileError::Compile(CompileError { expression, .. })) => expression,
227 /// _ => unreachable!(),
228 /// };
229 /// assert_eq!(index, Some(ExpressionIndex(0)));
230 /// # Ok(())
231 /// # }
232 /// ```
233 ///
234 /// Note that while this uses the same [`ExpressionIndex`] type as in
235 /// [`Match`](crate::matchers::Match), the value is *not*
236 /// calculated from any [`ExprId`](crate::expression::ExprId) instances
237 /// provided to
238 /// [`ExpressionSet::with_ids()`](crate::expression::ExpressionSet::with_ids),
239 /// but instead just from the expression's index in the set:
240 ///
241 ///```
242 /// # fn main() -> Result<(), vectorscan::error::VectorscanError> {
243 /// use vectorscan::{expression::*, error::*, matchers::*, flags::*};
244 ///
245 /// let e1: Expression = "aa".parse()?;
246 /// let e2: Expression = "as(df".parse()?;
247 /// let set = ExpressionSet::from_exprs([&e1, &e2]).with_ids([ExprId(2), ExprId(3)]);
248 /// let index = match set.compile(Mode::BLOCK) {
249 /// Err(VectorscanCompileError::Compile(CompileError { expression, .. })) => expression,
250 /// _ => unreachable!(),
251 /// };
252 /// assert_eq!(index, Some(ExpressionIndex(1)));
253 /// # Ok(())
254 /// # }
255 /// ```
256 ///
257 /// If the error is not specific to an expression, then this value will be
258 /// [`None`]:
259 ///```
260 /// // Using vectorscan::alloc requires the "alloc" feature.
261 /// #[cfg(feature = "alloc")]
262 /// fn main() -> Result<(), vectorscan::error::VectorscanError> {
263 /// use vectorscan::{expression::*, error::*, flags::*, alloc::*};
264 /// use std::{alloc::{GlobalAlloc, Layout}, ptr};
265 ///
266 /// // Create a broken allocator:
267 /// struct S;
268 /// unsafe impl GlobalAlloc for S {
269 /// unsafe fn alloc(&self, _layout: Layout) -> *mut u8 { ptr::null_mut() }
270 /// unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {}
271 /// }
272 /// // Set it as the db compile allocator:
273 /// assert!(set_db_allocator(LayoutTracker::new(S.into())).unwrap().is_none());
274 ///
275 /// let expr: Expression = "a".parse()?;
276 /// let CompileError { message, expression } = match expr.compile(Flags::default(), Mode::BLOCK) {
277 /// Err(VectorscanCompileError::Compile(err)) => err,
278 /// _ => unreachable!(),
279 /// };
280 /// assert_eq!(expression, None);
281 /// assert_eq!(&message, "Could not allocate memory for bytecode.");
282 /// Ok(())
283 /// }
284 /// # #[cfg(not(feature = "alloc"))]
285 /// # fn main() {}
286 /// ```
287 pub expression: Option<ExpressionIndex>,
288}
289
290#[cfg(feature = "compiler")]
291impl fmt::Display for CompileError {
292 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
293 write!(
294 f,
295 "compile error(@{:?}): {}",
296 &self.expression, &self.message
297 )
298 }
299}
300
301#[cfg(feature = "compiler")]
302impl CompileError {
303 pub(crate) fn copy_from_native(
304 x: &mut hs::hs_compile_error,
305 ) -> Result<Self, VectorscanRuntimeError> {
306 let hs::hs_compile_error {
307 message,
308 expression,
309 } = x;
310 assert!(!message.is_null());
311 let ret = Self {
312 message: unsafe { CStr::from_ptr(*message) }
313 .to_string_lossy()
314 .to_string(),
315 expression: if *expression < 0 {
316 None
317 } else {
318 Some(ExpressionIndex(*expression as c_uint))
319 },
320 };
321 VectorscanRuntimeError::from_native(unsafe { hs::hs_free_compile_error(x) })?;
322 Ok(ret)
323 }
324}
325
326/// Wrapper for errors returned when parsing or compiling expressions.
327#[cfg(feature = "compiler")]
328#[cfg_attr(docsrs, doc(cfg(feature = "compiler")))]
329#[derive(Debug, Display, Error)]
330pub enum VectorscanCompileError {
331 /// non-compilation error: {0}
332 NonCompile(#[from] VectorscanRuntimeError),
333 /// pattern compilation error: {0}
334 Compile(#[from] CompileError),
335 /// null byte in expression: {0}
336 NullByte(#[from] NulError),
337}
338
339/// Failure to compress a stream into a buffer.
340#[derive(Debug, Display, Error)]
341pub enum CompressionError {
342 /// other error: {0}
343 Other(#[from] VectorscanRuntimeError),
344 /// not enough space for {0} in buf {1:?}
345 NoSpace(usize, Vec<u8>),
346}
347
348/// Wrapper for errors returned by
349/// [`Scratch::scan_channel()`](crate::state::Scratch::scan_channel) and other
350/// async scanning methods.
351#[cfg(feature = "async")]
352#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
353#[derive(Debug, Display, Error)]
354pub enum ScanError {
355 /// error from return value of `hs_scan*()`: {0}
356 ReturnValue(#[from] VectorscanRuntimeError),
357 /// task join error: {0}
358 Join(#[from] tokio::task::JoinError),
359}
360
361/// Top-level wrapper for errors returned by this library.
362#[derive(Debug, Display, Error)]
363#[ignore_extra_doc_attributes]
364pub enum VectorscanError {
365 /// error from the vectorscan runtime: {0}
366 Runtime(#[from] VectorscanRuntimeError),
367 /// compile error: {0}
368 #[cfg(feature = "compiler")]
369 #[cfg_attr(docsrs, doc(cfg(feature = "compiler")))]
370 Compile(#[from] VectorscanCompileError),
371 /// error during scan: {0}
372 #[cfg(feature = "async")]
373 #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
374 Scan(#[from] ScanError),
375 /// error compressing stream: {0}
376 Compression(#[from] CompressionError),
377}
378
379/// Errors returned by methods in the chimera library.
380#[cfg(feature = "chimera")]
381#[cfg_attr(docsrs, doc(cfg(feature = "chimera")))]
382pub mod chimera {
383 use crate::{hs, matchers::ExpressionIndex};
384
385 use displaydoc::Display;
386 use thiserror::Error;
387
388 use std::{
389 ffi::{CStr, NulError},
390 fmt,
391 os::raw::c_uint,
392 };
393
394 /// Native error code from the underlying chimera library.
395 #[derive(
396 Debug,
397 Display,
398 Error,
399 Copy,
400 Clone,
401 PartialEq,
402 Eq,
403 PartialOrd,
404 Ord,
405 Hash,
406 num_enum::IntoPrimitive,
407 num_enum::FromPrimitive,
408 )]
409 #[repr(i8)]
410 #[ignore_extra_doc_attributes]
411 pub enum ChimeraRuntimeError {
412 /// A parameter passed to this function was invalid.
413 Invalid = hs::CH_INVALID,
414 /// A memory allocation failed.
415 NoMem = hs::CH_NOMEM,
416 /// The engine was terminated by callback.
417 ///
418 /// This return value indicates that the target buffer was partially
419 /// scanned, but that the callback function returned
420 /// [`ChimeraMatchResult::Terminate`](crate::matchers::chimera::ChimeraMatchResult::Terminate)
421 /// after a match was located.
422 ScanTerminated = hs::CH_SCAN_TERMINATED,
423 /// The pattern compiler failed, and the [`ChimeraInnerCompileError`] should
424 /// be inspected for more detail.
425 CompilerError = hs::CH_COMPILER_ERROR,
426 /// The given database was built for a different version of the Chimera
427 /// matcher.
428 DbVersionError = hs::CH_DB_VERSION_ERROR,
429 /// The given database was built for a different platform (i.e., CPU type).
430 DbPlatformError = hs::CH_DB_PLATFORM_ERROR,
431 /// The given database was built for a different mode of operation.
432 ///
433 /// This error is returned when streaming calls are used with a
434 /// non-streaming database and vice versa.
435 DbModeError = hs::CH_DB_MODE_ERROR,
436 /// A parameter passed to this function was not correctly aligned.
437 BadAlign = hs::CH_BAD_ALIGN,
438 /// The memory allocator did not correctly return memory suitably aligned
439 /// for the largest representable data type on this platform.
440 BadAlloc = hs::CH_BAD_ALLOC,
441 /// The scratch region was already in use.
442 ///
443 /// This error is returned when Chimera is able to detect that the scratch
444 /// region given is already in use by another Chimera API call.
445 ///
446 /// A separate scratch region, allocated with
447 /// [`ChimeraScratch::setup_for_db()`](crate::state::chimera::ChimeraScratch::setup_for_db)
448 /// or [`ChimeraScratch::try_clone()`](crate::state::chimera::ChimeraScratch::try_clone), is
449 /// required for every concurrent caller of the Chimera API.
450 ///
451 /// For example, this error might be returned when
452 /// [`ChimeraScratch::scan_sync()`](crate::state::chimera::ChimeraScratch::scan_sync)
453 /// has been called inside a callback delivered by a currently-executing
454 /// [`ChimeraScratch::scan_sync()`](crate::state::chimera::ChimeraScratch::scan_sync)
455 /// call using the same scratch region.
456 ///
457 /// Note: Not all concurrent uses of scratch regions may be detected. This
458 /// error is intended as a best-effort debugging tool, not a guarantee.
459 ///
460 /// Note: safe Rust code should never see this error. See [`crate::state`]
461 /// for ways to manage scratch spaces.
462 ScratchInUse = hs::CH_SCRATCH_IN_USE,
463 /// Unexpected internal error from Vectorscan.
464 ///
465 /// This error indicates that there was unexpected matching behaviors from
466 /// Vectorscan. This could be related to invalid usage of scratch space or
467 /// invalid memory operations by users.
468 #[num_enum(default)]
469 UnknownError = hs::CH_UNKNOWN_HS_ERROR,
470 /// Returned when pcre_exec (called for some expressions internally from
471 /// [`ChimeraScratch::scan_sync()`](crate::state::chimera::ChimeraScratch::scan_sync))
472 /// failed due to a fatal error.
473 FailInternal = hs::CH_FAIL_INTERNAL,
474 }
475
476 impl ChimeraRuntimeError {
477 pub(crate) fn from_native(x: hs::ch_error_t) -> Result<(), Self> {
478 static_assertions::const_assert_eq!(0, hs::CH_SUCCESS);
479 if x == 0 {
480 Ok(())
481 } else {
482 let s: Self = (x as i8).into();
483 Err(s)
484 }
485 }
486
487 #[cfg(feature = "compiler")]
488 pub(crate) fn copy_from_native_compile_error(
489 x: hs::ch_error_t,
490 c: *mut hs::ch_compile_error,
491 ) -> Result<(), ChimeraCompileError> {
492 match Self::from_native(x) {
493 Ok(()) => Ok(()),
494 Err(Self::CompilerError) => {
495 let e = ChimeraInnerCompileError::copy_from_native(unsafe { &mut *c }).unwrap();
496 Err(ChimeraCompileError::Compile(e))
497 },
498 Err(e) => Err(e.into()),
499 }
500 }
501 }
502
503 /// Error details returned by the pattern compiler.
504 ///
505 /// This is returned by the compile calls
506 /// ([`ChimeraDb::compile()`](crate::database::chimera::ChimeraDb::compile)
507 /// and [`ChimeraDb::compile_multi()`](crate::database::chimera::ChimeraDb::compile_multi)) on
508 /// failure. The caller may inspect the values returned in this type to
509 /// determine the cause of failure.
510 #[derive(Debug, Error)]
511 #[cfg(feature = "compiler")]
512 #[cfg_attr(docsrs, doc(cfg(feature = "compiler")))]
513 pub struct ChimeraInnerCompileError {
514 /// A human-readable error message describing the error.
515 ///
516 /// Common errors are the same as for the base vectorscan library's
517 /// [`super::CompileError::message`], except that PCRE constructs are fully
518 /// supported and will not cause errors.
519 pub message: String,
520 /// The zero-based number of the expression that caused the error (if this
521 /// can be determined). This value's behavior is the same as for the base
522 /// vectorscan library's [`super::CompileError::expression`].
523 pub expression: Option<ExpressionIndex>,
524 }
525
526 #[cfg(feature = "compiler")]
527 impl fmt::Display for ChimeraInnerCompileError {
528 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
529 write!(
530 f,
531 "chimera compile error(@{:?}): {}",
532 &self.expression, &self.message
533 )
534 }
535 }
536
537 #[cfg(feature = "compiler")]
538 impl ChimeraInnerCompileError {
539 pub(crate) fn copy_from_native(
540 x: &mut hs::ch_compile_error,
541 ) -> Result<Self, ChimeraRuntimeError> {
542 let hs::ch_compile_error {
543 message,
544 expression,
545 } = x;
546 assert!(!message.is_null());
547 let ret = Self {
548 message: unsafe { CStr::from_ptr(*message) }
549 .to_string_lossy()
550 .to_string(),
551 expression: if *expression < 0 {
552 None
553 } else {
554 Some(ExpressionIndex(*expression as c_uint))
555 },
556 };
557 ChimeraRuntimeError::from_native(unsafe { hs::ch_free_compile_error(x) })?;
558 Ok(ret)
559 }
560 }
561
562 /// Wrapper for errors returned when parsing or compiling expressions.
563 #[cfg(feature = "compiler")]
564 #[cfg_attr(docsrs, doc(cfg(feature = "compiler")))]
565 #[derive(Debug, Display, Error)]
566 pub enum ChimeraCompileError {
567 /// non-compilation error: {0}
568 NonCompile(#[from] ChimeraRuntimeError),
569 /// pattern compilation error: {0}
570 Compile(#[from] ChimeraInnerCompileError),
571 /// null byte in expression: {0}
572 NullByte(#[from] NulError),
573 }
574
575 /// Native error code for non-fatal match errors from PCRE execution.
576 #[derive(
577 Debug,
578 Display,
579 Error,
580 Copy,
581 Clone,
582 PartialEq,
583 Eq,
584 PartialOrd,
585 Ord,
586 Hash,
587 num_enum::IntoPrimitive,
588 num_enum::TryFromPrimitive,
589 )]
590 #[repr(u8)]
591 pub enum ChimeraMatchErrorType {
592 /// PCRE hits its match limit and reports `PCRE_ERROR_MATCHLIMIT`.
593 MatchLimit = hs::CH_ERROR_MATCHLIMIT,
594 /// PCRE hits its recursion limit and reports `PCRE_ERROR_RECURSIONLIMIT`.
595 RecursionLimit = hs::CH_ERROR_RECURSIONLIMIT,
596 }
597
598 impl ChimeraMatchErrorType {
599 pub(crate) fn from_native(x: hs::ch_error_event_t) -> Self { (x as u8).try_into().unwrap() }
600 }
601
602 /// Error type for non-fatal match errors from PCRE execution during
603 /// [`ChimeraScratch::scan_sync()`](crate::state::chimera::ChimeraScratch::scan_sync).
604 #[derive(Debug, Error)]
605 pub struct ChimeraMatchError {
606 /// The type of error that occurred.
607 #[source]
608 pub error_type: ChimeraMatchErrorType,
609 /// The ID number of the expression that failed.
610 pub id: ExpressionIndex,
611 }
612
613 impl fmt::Display for ChimeraMatchError {
614 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
615 write!(f, "{}@{}", self.error_type, self.id)
616 }
617 }
618
619 /// Wrapper for errors returned by
620 /// [`ChimeraScratch::scan_channel()`](crate::state::chimera::ChimeraScratch::scan_channel).
621 #[cfg(feature = "async")]
622 #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
623 #[derive(Debug, Display, Error)]
624 pub enum ChimeraScanError {
625 /// error from return value of `ch_scan()`: {0}
626 ReturnValue(#[from] ChimeraRuntimeError),
627 /// non-fatal match error: {0}
628 MatchError(#[from] ChimeraMatchError),
629 /// task join error: {0}
630 Join(#[from] tokio::task::JoinError),
631 }
632
633 /// Top-level wrapper for errors returned by the chimera library.
634 #[derive(Debug, Display, Error)]
635 #[ignore_extra_doc_attributes]
636 pub enum ChimeraError {
637 /// error from chimera runtime: {0}
638 Runtime(#[from] ChimeraRuntimeError),
639 /// error from vectorscan runtime: {0}
640 ///
641 /// This case in particular is helpful to convert the result of
642 /// [`Platform::local()`](crate::flags::platform::Platform::local) into a
643 /// chimera error.
644 #[cfg(feature = "compiler")]
645 #[cfg_attr(docsrs, doc(cfg(feature = "compiler")))]
646 VectorscanRuntime(#[from] super::VectorscanRuntimeError),
647 /// compile error: {0}
648 #[cfg(feature = "compiler")]
649 #[cfg_attr(docsrs, doc(cfg(feature = "compiler")))]
650 Compile(#[from] ChimeraCompileError),
651 /// error during chimera scan: {0}
652 #[cfg(feature = "async")]
653 #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
654 Scan(#[from] ChimeraScanError),
655 }
656}