queue_file/error.rs
1//! Error types and utilities for [`QueueFile`] operations.
2//!
3//! This module provides the [`Error`] enum which represents all possible errors
4//! that can occur during queue file operations, along with the [`Result`] type
5//! alias and helper macros.
6//!
7//! # Error Categories
8//!
9//! - **I/O Errors**: File system and read/write failures
10//! - **Validation Errors**: Corrupted files, invalid data, sequence mismatches
11//! - **Capacity Errors**: Element too big, too many elements
12//! - **Format Errors**: Unsupported versions, magic mismatches
13
14use std::io;
15
16use thiserror::Error;
17
18/// The error type for [`QueueFile`] operations.
19///
20/// Each variant represents a distinct category of failure that can occur
21/// when working with queue files.
22#[derive(Debug, Error)]
23pub enum Error {
24 /// An I/O error occurred while reading or writing the queue file.
25 #[error(transparent)]
26 Io {
27 #[from]
28 source: io::Error,
29 },
30
31 /// The queue contains more than `i32::MAX` elements.
32 ///
33 /// This is a limitation of the on-disk format.
34 #[error("too many elements")]
35 TooManyElements,
36
37 /// An individual element exceeds the maximum allowed size.
38 ///
39 /// Elements are limited to `i32::MAX` bytes.
40 #[error("element too big")]
41 ElementTooBig,
42
43 /// The queue file contains corrupted data.
44 ///
45 /// This can indicate a hardware failure, incomplete write, or
46 /// manual tampering with the file.
47 #[error("corrupted file: {msg}")]
48 CorruptedFile { msg: String },
49
50 /// The file header magic bytes don't match the expected format.
51 #[error("magic mismatch: expected {expected:#010x}, found {found:#010x}")]
52 MagicMismatch { expected: u32, found: u32 },
53
54 /// A CRC-32 checksum verification failed.
55 ///
56 /// This indicates data corruption in the element header or payload.
57 #[error("checksum mismatch: expected {expected:#010x}, found {found:#010x}")]
58 ChecksumMismatch { expected: u32, found: u32 },
59
60 /// A value in the queue file is invalid or out of expected range.
61 #[error("invalid value: {msg}")]
62 InvalidValue { msg: String },
63
64 /// A calculated position is outside the valid file bounds.
65 #[error("out of bounds: {msg}")]
66 OutOfBounds { msg: String },
67
68 /// Element sequence numbers are not in the expected order.
69 ///
70 /// This indicates a corrupted queue or recovery failure in v2 format.
71 #[error("sequence mismatch: expected {expected}, found {found}")]
72 SequenceMismatch { expected: u64, found: u64 },
73
74 /// The queue file format version is not supported.
75 #[error("unsupported version {detected}. supported versions is {supported} and legacy")]
76 UnsupportedVersion { detected: u32, supported: u32 },
77}
78
79/// A specialized [`Result`] type for [`QueueFile`] operations.
80/// The result type for queue file operations, with [`Error`] as the default error type.
81pub type Result<T, E = Error> = std::result::Result<T, E>;
82
83/// Ensures that a condition is true, returning an error if it is not.
84///
85/// This macro provides a convenient way to validate preconditions
86/// and invariants within [`QueueFile`] methods.
87///
88/// # Variants
89///
90/// - `ensure!(condition, VariantName { field: value })` - creates error with fields
91/// - `ensure!(condition, VariantName)` - creates error with no fields
92/// - `ensure!(condition, expression)` - uses the provided expression as error
93#[macro_export]
94macro_rules! ensure {
95 ($cond:expr, $variant:ident { $($field:ident : $val:expr),* $(,)? }) => {
96 if !($cond) {
97 return Err($crate::Error::$variant { $($field : $val),* });
98 }
99 };
100 ($cond:expr, $variant:ident) => {
101 if !($cond) {
102 return Err($crate::Error::$variant);
103 }
104 };
105 ($cond:expr, $err:expr) => {
106 if !($cond) {
107 return Err($err);
108 }
109 };
110}
111
112/// Injects a test failure at the given failpoint if the environment variable is set.
113///
114/// This function is used for testing crash recovery and atomicity guarantees.
115/// When `QUEUE_FILE_FAILPOINT` environment variable matches the failpoint name,
116/// the function returns an error, simulating a crash at that point.
117///
118/// This is only available in test builds for safety reasons.
119#[inline]
120pub fn maybe_inject_failpoint(name: &str) -> Result<()> {
121 if std::env::var("QUEUE_FILE_FAILPOINT").ok().as_deref() == Some(name) {
122 return Err(Error::CorruptedFile { msg: format!("injected failpoint: {name}") });
123 }
124
125 Ok(())
126}