1use thiserror::Error;
4
5pub(crate) fn panic_payload_detail(panic: Box<dyn std::any::Any + Send>) -> String {
8 if let Some(message) = panic.downcast_ref::<String>() {
9 message.clone()
10 } else if let Some(message) = panic.downcast_ref::<&'static str>() {
11 (*message).to_string()
12 } else {
13 "non-string panic payload".to_string()
14 }
15}
16
17#[derive(Debug, Error)]
18pub enum ScanError {
20 #[error(
21 "failed to compile regex for detector {detector_id} pattern {index}: {source}. Fix: correct the detector regex or capture group configuration"
22 )]
23 RegexCompile {
24 detector_id: String,
25 index: usize,
26 source: regex::Error,
27 },
28 #[error(
29 "detector {detector_id} pattern {index} declares capture group {group}, but its compiled regex has only {captures_len} group(s) (valid indices 0..{captures_len}). \
30 An out-of-range group makes the engine fall back to the whole match, capturing the keyword and separator instead of the secret. \
31 Fix: set `group` to a capture-group index that exists in the regex (group 0 is the whole match), or add the missing capture group"
32 )]
33 CaptureGroupOutOfRange {
34 detector_id: String,
35 index: usize,
36 group: usize,
37 captures_len: usize,
38 },
39 #[error(
40 "detector {detector_id} pattern {index} has invalid compiled policy: {reason}. Fix: correct the detector TOML pattern policy"
41 )]
42 DetectorPatternPolicy {
43 detector_id: String,
44 index: usize,
45 reason: String,
46 },
47 #[error(
48 "failed to compile scanner regex set: {0}. Fix: simplify the detector regex set or remove the invalid pattern"
49 )]
50 RegexSetCompile(#[from] regex::Error),
51 #[error(
52 "failed to build Aho-Corasick literal matcher: {0}. Fix: check for empty or invalid detector keywords"
53 )]
54 AhoCorasick(#[from] aho_corasick::BuildError),
55 #[error(
56 "GPU scanner failure: {0}. Fix: rerun with `--backend cpu` to scan on the CPU path, or run `keyhog doctor` to diagnose the GPU stack"
57 )]
58 Gpu(String),
59 #[error(
60 "SIMD scanner failure: {0}. Fix: rerun with `--backend cpu` for the portable scalar path, or run `keyhog doctor` to check CPU feature detection"
61 )]
62 Simd(String),
63 #[error(
64 "compiled scanner invariant violation: {table}[{pattern_index}] references detector_index {detector_index} but only {detectors_len} detector(s) are loaded. Fix: rebuild detector compilation so every compiled pattern keeps its source detector index before scanner construction completes"
65 )]
66 CompiledPatternDetectorIndex {
67 table: &'static str,
68 pattern_index: usize,
69 detector_index: usize,
70 detectors_len: usize,
71 },
72 #[error(
73 "phase-one admission plan identity rejected: {0}. Fix: rebuild the plan for the live chunk batch or use the recovery-aware dispatch API to retain its exact recovery receipt"
74 )]
75 AdmissionPlanIdentity(String),
76 #[error("scanner configuration failure: {0}. Fix: correct the bundled scanner rules")]
77 Config(String),
78}
79
80pub type Result<T> = std::result::Result<T, ScanError>;