Skip to main content

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}