srt_runtime/error.rs
1//! Error type for SRT packet parsing and serialization.
2//!
3//! Spec grounding: `draft-sharabayko-srt-01` §3 (Packet Structure).
4
5/// Result alias for SRT packet parsing/serialization.
6pub type Result<T> = core::result::Result<T, Error>;
7
8/// An SRT packet parse / serialize error.
9#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
10#[non_exhaustive]
11pub enum Error {
12 /// Input shorter than required.
13 #[error("buffer too short: need {need}, have {have} ({what})")]
14 BufferTooShort {
15 /// Bytes required.
16 need: usize,
17 /// Bytes available.
18 have: usize,
19 /// What was being parsed.
20 what: &'static str,
21 },
22 /// The output buffer passed to `serialize_into` was too small.
23 #[error("output buffer too small: need {need}, have {have}")]
24 OutputBufferTooSmall {
25 /// Bytes required.
26 need: usize,
27 /// Bytes available.
28 have: usize,
29 },
30 /// A field value did not fit in its wire bit-width.
31 #[error("field {what} value {value} does not fit in {bits} bits")]
32 FieldTooWide {
33 /// The over-wide field name.
34 what: &'static str,
35 /// The offending value.
36 value: u64,
37 /// The field width on the wire.
38 bits: u32,
39 },
40 /// The Control Information Field length did not match any known ACK
41 /// variant (Full / Small / Light — `draft-sharabayko-srt-01` §3.2.4).
42 #[error("ACK CIF length {len} does not match Full (28), Small (16), or Light (4)")]
43 InvalidAckLength {
44 /// The CIF length actually found, in bytes.
45 len: usize,
46 },
47 /// A NAK loss-list (`draft-sharabayko-srt-01` Appendix A) was not a whole
48 /// number of 4-byte entries, or a range entry's second word had its top
49 /// bit set (which would make it a nested range).
50 #[error("invalid NAK loss list: {reason}")]
51 InvalidLossList {
52 /// Why the loss list is invalid.
53 reason: &'static str,
54 },
55 /// A Handshake Extension block's declared length (in 4-byte units)
56 /// overran the remaining CIF bytes (`draft-sharabayko-srt-01` §3.2.1).
57 #[error("handshake extension length {declared} * 4 bytes overruns {remaining} remaining")]
58 ExtensionOverrun {
59 /// The declared `Extension Length` (in 4-byte blocks).
60 declared: u16,
61 /// The bytes actually remaining in the CIF.
62 remaining: usize,
63 },
64 /// The Stream ID extension contents were not valid UTF-8 after undoing the
65 /// 32-bit little-endian word storage (`draft-sharabayko-srt-01` §3.2.1.3).
66 #[error("invalid Stream ID extension UTF-8")]
67 InvalidStreamIdUtf8,
68 /// A Key Material message (`draft-sharabayko-srt-01` §3.2.2) fixed-value
69 /// field did not carry its mandated value.
70 #[error("invalid key material {field}: {reason}")]
71 InvalidKeyMaterial {
72 /// The offending field.
73 field: &'static str,
74 /// Why it is invalid.
75 reason: &'static str,
76 },
77 /// A type-specific `parse` was called on bytes whose `F` bit (or Control
78 /// Type) indicated the other packet kind.
79 #[error("wrong packet kind: expected {expected}")]
80 WrongPacketKind {
81 /// What the caller expected to parse.
82 expected: &'static str,
83 },
84 /// A field documented as reserved / must-be-zero (`draft-sharabayko-srt-01`
85 /// §3.2, e.g. `Subtype` on a defined control type, or the header
86 /// `Type-specific Information` word where the packet type does not use
87 /// it) carried a non-zero value.
88 #[error("reserved field {what} must be zero, found {value:#x}")]
89 ReservedFieldNotZero {
90 /// The offending field.
91 what: &'static str,
92 /// The non-zero value found.
93 value: u64,
94 },
95 /// A general structural constraint (not a bit-width overflow) was
96 /// violated — e.g. a length that must be a whole number of 4-byte words.
97 #[error("invalid {what}: {reason}")]
98 InvalidField {
99 /// The offending field/value.
100 what: &'static str,
101 /// Why it is invalid.
102 reason: &'static str,
103 },
104 /// A Control Information Field documented as absent (Keep-Alive,
105 /// Congestion Warning, Shutdown, ACKACK, Peer Error — §3.2) carried extra
106 /// bytes.
107 #[error("{what} has {extra} unexpected trailing byte(s)")]
108 UnexpectedTrailingBytes {
109 /// What was being parsed.
110 what: &'static str,
111 /// How many bytes were left over.
112 extra: usize,
113 },
114 /// The handshake state machine (`crate::caller`/`crate::listener`) was
115 /// fed a control packet that is not a Handshake packet
116 /// (`draft-sharabayko-srt-01` §3.2.1) — only Handshake packets
117 /// participate in the exchange modeled there.
118 #[error("expected a Handshake control packet, got {actual}")]
119 UnexpectedControlPacket {
120 /// The `Control Type` label of the packet actually fed.
121 actual: &'static str,
122 },
123 /// A handshake state-machine call happened in a state where the draft's
124 /// flow (`draft-sharabayko-srt-01` §4.3.1) does not expect it — a driver
125 /// bug (e.g. calling `start()` twice, or `feed()` after `Connected`),
126 /// not a peer protocol failure.
127 #[error("handshake call not valid in state {state}: {reason}")]
128 HandshakeOutOfSequence {
129 /// The state the handshake was in.
130 state: &'static str,
131 /// Why the call is not valid there.
132 reason: &'static str,
133 },
134 /// A real-socket I/O failure surfaced by [`crate::io`]'s tokio adapter
135 /// (feature `tokio`, which implies `std`). Carries the OS
136 /// [`std::io::ErrorKind`] — so e.g. a bind failure (`AddrInUse`) is
137 /// distinguishable from a mid-connection reset (`ConnectionReset`) — plus
138 /// a fixed `context` naming the failing call site (`"bind"`, `"connect"`,
139 /// `"recv"`, `"send"`, …). `std::io::Error` itself is not
140 /// `Clone`/`PartialEq`/`Eq` (this enum derives all three), so only its
141 /// `kind()` is kept rather than the full error.
142 #[cfg(feature = "std")]
143 #[error("io error during {context}: {kind:?}")]
144 Io {
145 /// The `std::io::Error::kind()` of the underlying OS error.
146 kind: std::io::ErrorKind,
147 /// Which call site failed (e.g. `"bind"`, `"connect"`, `"recv"`,
148 /// `"send"`).
149 context: &'static str,
150 },
151}