page_db/error.rs
1//! The crate error type.
2
3use std::io;
4
5/// A convenience alias for results returned by this crate.
6pub type PageResult<T> = Result<T, PageError>;
7
8/// Everything that can go wrong reading, writing, or framing a page.
9///
10/// The variants split into two families: I/O failures from the underlying file
11/// ([`PageError::Io`]) and integrity failures detected while validating a page
12/// against its header. The latter are the interesting ones — they are how a
13/// torn write, a bit-rotted block, or a misdirected read surfaces as a value
14/// instead of silently corrupting the layer above.
15///
16/// The type is `#[non_exhaustive]`: later releases may add variants (for
17/// example as the allocator and buffer pool land), so match with a wildcard
18/// arm.
19///
20/// # Examples
21///
22/// ```
23/// use page_db::PageError;
24///
25/// // I/O errors convert in with `?` via the `From<std::io::Error>` impl.
26/// fn classify(err: &PageError) -> &'static str {
27/// match err {
28/// PageError::Io(_) => "io",
29/// PageError::ChecksumMismatch { .. } => "corruption",
30/// _ => "other",
31/// }
32/// }
33/// ```
34#[derive(Debug, thiserror::Error)]
35#[non_exhaustive]
36pub enum PageError {
37 /// An I/O operation on the underlying file failed.
38 ///
39 /// The source [`std::io::Error`] is preserved, so callers that need the OS
40 /// error kind (for example to distinguish [`io::ErrorKind::NotFound`] on
41 /// open) can reach it through [`std::error::Error::source`] or by matching
42 /// this variant directly.
43 #[error("page i/o error: {0}")]
44 Io(#[from] io::Error),
45
46 /// A requested page size is not a power of two within
47 /// [`MIN_PAGE_SIZE`](crate::MIN_PAGE_SIZE)..=[`MAX_PAGE_SIZE`](crate::MAX_PAGE_SIZE).
48 #[error(
49 "invalid page size {size}: must be a power of two in {min}..={max}",
50 min = crate::MIN_PAGE_SIZE,
51 max = crate::MAX_PAGE_SIZE
52 )]
53 InvalidPageSize {
54 /// The rejected size, in bytes.
55 size: usize,
56 },
57
58 /// The page's leading magic bytes did not match. The block is not a
59 /// page-db page, or its first bytes are corrupt.
60 #[error("bad page magic: found {found:#010x}, expected {expected:#010x}")]
61 BadMagic {
62 /// The magic value read from disk.
63 found: u32,
64 /// The magic value this build writes.
65 expected: u32,
66 },
67
68 /// The page's format version is newer than this build understands.
69 #[error("unsupported page format version {found} (this build writes {supported})")]
70 UnsupportedVersion {
71 /// The version read from disk.
72 found: u16,
73 /// The version this build writes and can read.
74 supported: u16,
75 },
76
77 /// The page's stored CRC32C did not match the checksum recomputed over its
78 /// bytes. The page is corrupt — a torn write, bit rot, or a wrong byte.
79 #[error(
80 "checksum mismatch on page {page_id}: stored {stored:#010x}, computed {computed:#010x}"
81 )]
82 ChecksumMismatch {
83 /// The id of the slot that was read.
84 page_id: u64,
85 /// The checksum stored in the page header.
86 stored: u32,
87 /// The checksum recomputed over the page on read.
88 computed: u32,
89 },
90
91 /// The page read back from a slot carries a different id than the one
92 /// requested. This catches a misdirected read or write — the file handed
93 /// back the wrong block.
94 #[error("misdirected page: slot {requested} holds a page stamped {found}")]
95 MisdirectedPage {
96 /// The slot id that was read.
97 requested: u64,
98 /// The id stamped in the page header found there.
99 found: u64,
100 },
101
102 /// A read returned fewer bytes than a whole page. The slot is past the end
103 /// of the file, or the file's length is not a whole number of pages.
104 #[error("short read on page {page_id}: got {got} of {page_size} bytes")]
105 ShortRead {
106 /// The slot id that was read.
107 page_id: u64,
108 /// The number of bytes actually read.
109 got: usize,
110 /// The configured page size.
111 page_size: usize,
112 },
113
114 /// A buffer pool could not admit another page because every frame is
115 /// pinned. The pool refuses to evict a pinned page, so this is the signal to
116 /// release some pins or size the pool larger.
117 #[error("buffer pool exhausted: all {capacity} frames are pinned")]
118 BufferPoolExhausted {
119 /// The pool's frame capacity.
120 capacity: usize,
121 },
122
123 /// An id handed to the allocator's `free` is not one it could have
124 /// allocated: it is the reserved superblock (page 0), or it is beyond the
125 /// high-water mark of pages ever allocated.
126 #[error("invalid page id to free: {page_id}")]
127 InvalidPageId {
128 /// The rejected id.
129 page_id: u64,
130 },
131
132 /// The file's page 0 is not a valid allocator superblock. The file was not
133 /// initialized by the allocator, or its superblock is corrupt.
134 #[error("page 0 is not a valid allocator superblock")]
135 InvalidSuperblock,
136}