Skip to main content

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}