segment_buffer/error.rs
1//! Error types for segment-buffer.
2//!
3//! Errors carry the context an operator needs to diagnose a failure at 3am:
4//! the path to the offending segment file, the phase that failed, and the
5//! underlying cause. Use [`Result`](crate::Result) as the alias.
6//!
7//! # Matching on a failure to recover the offending path
8//!
9//! Every non-I/O variant carries the segment file's [`PathBuf`](std::path::PathBuf),
10//! so an operator can match on the variant and act (move the bad file aside,
11//! alert, etc.) without parsing the rendered message:
12//!
13//! ```
14//! use segment_buffer::{SegmentBuffer, SegmentConfig, SegmentError};
15//! use tempfile::tempdir;
16//!
17//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
18//! let dir = tempdir()?;
19//! let buf: SegmentBuffer<u64> =
20//! SegmentBuffer::open(dir.path(), SegmentConfig::default())?;
21//!
22//! // Drop a corrupt segment so the next read surfaces a typed error.
23//! std::fs::write(
24//! dir.path().join("seg_000000000000_000000000000.zst"),
25//! b"this is not zstd+CBOR",
26//! )?;
27//!
28//! match buf.read_from(0, 10) {
29//! Ok(_) => { /* happy path */ }
30//! Err(SegmentError::Cbor { path, phase, .. }) => {
31//! eprintln!(
32//! "CBOR {phase} failed on {}; quarantining",
33//! path.display()
34//! );
35//! let quarantined = format!("{}.quarantined", path.display());
36//! let _ = std::fs::rename(&path, quarantined);
37//! }
38//! Err(SegmentError::Cipher { path, .. }) => {
39//! eprintln!("cipher failure on {} — likely wrong key", path.display());
40//! }
41//! Err(SegmentError::Integrity { path, reason }) => {
42//! eprintln!("integrity failure on {}: {reason}", path.display());
43//! }
44//! Err(SegmentError::Io(e)) => {
45//! eprintln!("unrelated I/O failure: {e}");
46//! }
47//! // `SegmentError` is `#[non_exhaustive]`, so a catch-all is required
48//! // for forward compatibility with future variants.
49//! Err(other) => {
50//! eprintln!("unhandled segment-buffer error: {other}");
51//! }
52//! }
53//! # Ok(())
54//! # }
55//! ```
56
57use std::path::PathBuf;
58
59/// Errors produced by segment-buffer operations.
60///
61/// Every non-I/O variant carries the [`path`](Self::Cbor) of the segment file
62/// involved, so an operator can act on the failure without spelunking through
63/// logs.
64#[derive(Debug, thiserror::Error)]
65#[non_exhaustive]
66pub enum SegmentError {
67 /// Filesystem I/O failure (directory creation, segment read/write, rename, etc.).
68 ///
69 /// Kept as a tuple variant with `#[from]` so `?` remains ergonomic at the
70 /// many I/O call sites where per-site context would add noise without value.
71 #[error("I/O error: {0}")]
72 Io(#[from] std::io::Error),
73
74 /// CBOR serialization or deserialization of a segment file failed.
75 #[error("CBOR {phase} failed for {path}: {message}")]
76 Cbor {
77 /// Which direction failed: `"serialize"` (writing) or `"deserialize"` (reading).
78 phase: &'static str,
79 /// Path to the offending segment file.
80 path: PathBuf,
81 /// Underlying CBOR error message.
82 message: String,
83 },
84
85 /// Cipher encrypt or decrypt of a segment file failed (key mismatch, AEAD
86 /// tag invalid, cipher misconfiguration).
87 #[error("cipher error for {path}: {message}")]
88 Cipher {
89 /// Path to the offending segment file.
90 path: PathBuf,
91 /// Underlying cipher error message.
92 message: String,
93 },
94
95 /// Segment file failed an integrity check: truncated, too small for the
96 /// AEAD nonce, or unrecognized envelope.
97 #[error("integrity failure for {path}: {reason}")]
98 Integrity {
99 /// Path to the offending segment file.
100 path: PathBuf,
101 /// What failed, in one short phrase.
102 reason: &'static str,
103 },
104}
105
106/// Result alias used throughout the crate.
107pub type Result<T> = std::result::Result<T, SegmentError>;