ferro_lumberjack/error.rs
1// SPDX-License-Identifier: Apache-2.0
2//! Error types.
3//!
4//! Two distinct error families:
5//!
6//! - [`FrameError`] — pure codec errors (no I/O). Returned by the
7//! [`crate::frame::FrameDecoder`] and the encoder helpers.
8//! - [`ProtocolError`] — higher-level errors emitted by the
9//! client (transport, TLS, ACK validation). Wraps `FrameError`
10//! and `std::io::Error`.
11
12use thiserror::Error;
13
14/// Errors that can occur while encoding or decoding a Lumberjack v2 frame.
15///
16/// All variants are non-exhaustive in spirit: future versions of the codec
17/// may add new variants without bumping the major version while in the
18/// `v0.0.x` series.
19#[derive(Debug, Error)]
20#[non_exhaustive]
21pub enum FrameError {
22 /// Wire byte 0 is not `b'2'`. The decoder refuses to advance because
23 /// it cannot determine which protocol version applies.
24 #[error("unsupported lumberjack version byte: {0:#x} (expected 0x32 / '2')")]
25 UnsupportedVersion(u8),
26
27 /// Wire byte 1 is not a known frame type.
28 #[error("unknown lumberjack frame type: {0:#x}")]
29 UnknownFrameType(u8),
30
31 /// A length field declared a frame larger than
32 /// [`crate::DEFAULT_MAX_FRAME_PAYLOAD`] (or the configured per-decoder
33 /// limit). Surfaces resource-exhaustion attacks before the decoder
34 /// allocates.
35 #[error("frame payload length {requested} exceeds configured limit of {limit} bytes")]
36 PayloadTooLarge {
37 /// Length declared on the wire.
38 requested: usize,
39 /// Configured cap for this decoder.
40 limit: usize,
41 },
42
43 /// Decompression failed (`C` frame had invalid zlib content).
44 #[error("zlib decompression failed: {0}")]
45 Decompression(String),
46
47 /// The decompressed contents of a `C` frame would exceed the
48 /// per-decoder size limit. Caps zlib-bomb input.
49 #[error("decompressed payload would exceed limit of {limit} bytes")]
50 DecompressedTooLarge {
51 /// Configured cap for this decoder.
52 limit: usize,
53 },
54
55 /// The compressor returned an error while encoding (typically out of
56 /// memory). Returned only by [`crate::encode_compressed`].
57 #[error("zlib compression failed: {0}")]
58 Compression(String),
59}
60
61/// Errors emitted by the high-level client.
62#[derive(Debug, Error)]
63#[non_exhaustive]
64pub enum ProtocolError {
65 /// I/O error while reading from or writing to the network.
66 #[error("io: {0}")]
67 Io(#[from] std::io::Error),
68
69 /// Codec error (frame parse / encode failure).
70 #[error("codec: {0}")]
71 Codec(#[from] FrameError),
72
73 /// Operation timed out (connect, write, or ACK read).
74 #[error("operation timed out: {0}")]
75 Timeout(&'static str),
76
77 /// The receiver's ACK had an unexpected version or frame type, or its
78 /// sequence number didn't match what the client just sent.
79 #[error(
80 "unexpected ack: version=0x{version:02x} type=0x{frame_type:02x} seq={acked_seq} (expected_last_seq={expected_seq})"
81 )]
82 UnexpectedAck {
83 /// Wire-byte 0.
84 version: u8,
85 /// Wire-byte 1.
86 frame_type: u8,
87 /// Sequence number on the ACK frame.
88 acked_seq: u32,
89 /// Sequence number this client expected the ACK to reference.
90 expected_seq: u32,
91 },
92
93 /// The receiver acknowledged fewer events than were sent (partial ACK).
94 /// Surfaces so the caller can decide whether to retry the unacked tail.
95 #[error("partial ack: {acked} of {sent} events acknowledged")]
96 PartialAck {
97 /// Number of events the receiver acknowledged.
98 acked: u32,
99 /// Number of events the client sent in this window.
100 sent: u32,
101 },
102
103 /// No hosts were configured for the client.
104 #[error("no hosts configured")]
105 NoHostsConfigured,
106
107 /// All configured hosts have been tried and all failed; the wrapped
108 /// error is the most recent failure.
109 #[error("all configured hosts failed; last error: {0}")]
110 AllHostsFailed(Box<Self>),
111
112 /// TLS configuration error (key/cert load, server-name parse, …).
113 #[cfg(feature = "tls")]
114 #[error("tls config: {0}")]
115 Tls(String),
116}