1use miette::Diagnostic;
4use thiserror::Error;
5use winreg_format::cells::CellOffset;
6
7#[derive(Debug, Error, Diagnostic)]
9pub enum HiveError {
10 #[error("Invalid regf signature — not a registry hive file")]
11 #[diagnostic(code(winreg::invalid_signature))]
12 InvalidSignature,
13
14 #[error("Base block checksum mismatch: expected {expected:#010X}, computed {computed:#010X}")]
15 #[diagnostic(code(winreg::checksum_mismatch))]
16 ChecksumMismatch { expected: u32, computed: u32 },
17
18 #[error("Unsupported REGF version: {major}.{minor}")]
19 #[diagnostic(code(winreg::unsupported_version))]
20 UnsupportedVersion { major: u32, minor: u32 },
21
22 #[error("Cell at offset {offset} extends beyond hbin boundary (cell size: {cell_size}, hbin ends at: {hbin_end})")]
23 #[diagnostic(code(winreg::cell_overflow))]
24 CellOverflow {
25 offset: CellOffset,
26 cell_size: u32,
27 hbin_end: u64,
28 },
29
30 #[error("Invalid cell signature at offset {offset}: expected {expected}, got [{byte0:#04X}, {byte1:#04X}]")]
31 #[diagnostic(code(winreg::invalid_cell_signature))]
32 InvalidCellSignature {
33 offset: CellOffset,
34 expected: &'static str,
35 byte0: u8,
36 byte1: u8,
37 },
38
39 #[error("Cell at offset {offset} is unallocated (free cell)")]
40 #[diagnostic(code(winreg::unallocated_cell))]
41 UnallocatedCell { offset: CellOffset },
42
43 #[error("Null cell offset encountered where a valid offset was expected")]
44 #[diagnostic(code(winreg::null_offset))]
45 NullOffset,
46
47 #[error("Hive bins data is truncated: expected {expected} bytes, got {actual}")]
48 #[diagnostic(code(winreg::truncated_hive))]
49 TruncatedHive { expected: u64, actual: u64 },
50
51 #[error("Invalid hbin at file offset {file_offset}: bad signature")]
52 #[diagnostic(code(winreg::invalid_hbin))]
53 InvalidHbin { file_offset: u64 },
54
55 #[error("Key not found: {path}")]
56 #[diagnostic(code(winreg::key_not_found))]
57 KeyNotFound { path: String },
58
59 #[error("Value not found: {name} under key {key_path}")]
60 #[diagnostic(code(winreg::value_not_found))]
61 ValueNotFound { name: String, key_path: String },
62
63 #[error("I/O error: {0}")]
64 #[diagnostic(code(winreg::io))]
65 Io(#[from] std::io::Error),
66}
67
68pub type Result<T> = std::result::Result<T, HiveError>;
70
71#[cfg(test)]
72mod tests {
73 use super::*;
74
75 #[test]
76 fn error_display_messages() {
77 let err = HiveError::ChecksumMismatch {
78 expected: 0x1234_5678,
79 computed: 0xDEAD_BEEF,
80 };
81 let msg = format!("{err}");
82 assert!(msg.contains("0x12345678"));
83 assert!(msg.contains("0xDEADBEEF"));
84 }
85
86 #[test]
87 fn error_is_send_sync() {
88 fn assert_send_sync<T: Send + Sync>() {}
89 assert_send_sync::<HiveError>();
90 }
91
92 #[test]
93 fn io_error_converts() {
94 let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file missing");
95 let hive_err: HiveError = io_err.into();
96 assert!(matches!(hive_err, HiveError::Io(_)));
97 }
98}