1use thiserror::Error;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11#[non_exhaustive]
12pub enum ProtocolErrorKind {
13 Malformed,
15 Unsupported,
17 Timeout,
19 Unauthorized,
21 Other,
23}
24
25impl ProtocolErrorKind {
26 fn as_str(self) -> &'static str {
27 match self {
28 ProtocolErrorKind::Malformed => "malformed",
29 ProtocolErrorKind::Unsupported => "unsupported",
30 ProtocolErrorKind::Timeout => "timeout",
31 ProtocolErrorKind::Unauthorized => "unauthorized",
32 ProtocolErrorKind::Other => "other",
33 }
34 }
35}
36
37#[derive(Debug, Error)]
41#[non_exhaustive]
42pub enum StreamError {
43 #[error("Protocol error ({kind}): {detail}", kind = kind.as_str())]
45 Protocol {
46 kind: ProtocolErrorKind,
48 detail: String,
50 },
51
52 #[error("Handshake failed: {0}")]
54 Handshake(String),
55
56 #[error("Connection closed unexpectedly")]
58 ConnectionClosed,
59
60 #[error("Stream '{stream_id}' not found in application '{app}'")]
62 StreamNotFound {
63 app: String,
65 stream_id: String,
67 },
68
69 #[error("Application '{0}' not found")]
71 AppNotFound(String),
72
73 #[error("Application '{0}' is already registered")]
75 AppAlreadyRegistered(String),
76
77 #[error("Unauthorized: {0}")]
79 Unauthorized(String),
80
81 #[error("Stream '{stream_id}' is already publishing in application '{app}'")]
83 StreamAlreadyPublishing {
84 app: String,
86 stream_id: String,
88 },
89
90 #[error("Publisher limit reached ({limit} active streams); rejecting new publish")]
92 PublisherLimitReached {
93 limit: usize,
95 },
96
97 #[error("Unsupported codec: {0:?}")]
99 UnsupportedCodec(String),
100
101 #[error("Codec error: {0}")]
103 Codec(String),
104
105 #[error("Transcoding error: {0}")]
107 Transcode(String),
108
109 #[error("Hardware acceleration unavailable: {0}")]
111 HwAccelUnavailable(String),
112
113 #[error("Pipeline error: {0}")]
115 Pipeline(String),
116
117 #[error("Storage error: {0}")]
119 Storage(String),
120
121 #[error("Object not found: {0}")]
123 StorageNotFound(String),
124
125 #[error("Cluster error: {0}")]
127 Cluster(String),
128
129 #[error("Node not found: {0}")]
131 NodeNotFound(String),
132
133 #[error("Configuration error: {0}")]
135 Config(String),
136
137 #[error("I/O error: {0}")]
139 Io(#[from] std::io::Error),
140
141 #[error("{0}")]
143 Other(String),
144}
145
146impl StreamError {
147 pub fn protocol(msg: impl Into<String>) -> Self {
149 Self::Protocol {
150 kind: ProtocolErrorKind::Other,
151 detail: msg.into(),
152 }
153 }
154
155 pub fn protocol_kind(kind: ProtocolErrorKind, msg: impl Into<String>) -> Self {
157 Self::Protocol {
158 kind,
159 detail: msg.into(),
160 }
161 }
162
163 pub fn codec(msg: impl Into<String>) -> Self {
165 Self::Codec(msg.into())
166 }
167
168 pub fn transcode(msg: impl Into<String>) -> Self {
170 Self::Transcode(msg.into())
171 }
172
173 pub fn storage(msg: impl Into<String>) -> Self {
175 Self::Storage(msg.into())
176 }
177
178 pub fn cluster(msg: impl Into<String>) -> Self {
180 Self::Cluster(msg.into())
181 }
182
183 pub fn config(msg: impl Into<String>) -> Self {
185 Self::Config(msg.into())
186 }
187
188 pub fn other(msg: impl Into<String>) -> Self {
190 Self::Other(msg.into())
191 }
192}
193
194#[cfg(test)]
195mod tests {
196 use super::*;
197
198 #[test]
199 fn protocol_helpers_set_kind_and_render_it() {
200 let e = StreamError::protocol("boom");
201 assert!(matches!(
202 e,
203 StreamError::Protocol {
204 kind: ProtocolErrorKind::Other,
205 ..
206 }
207 ));
208 assert_eq!(e.to_string(), "Protocol error (other): boom");
209
210 let e = StreamError::protocol_kind(ProtocolErrorKind::Timeout, "slow peer");
211 assert_eq!(e.to_string(), "Protocol error (timeout): slow peer");
212 }
213
214 #[test]
215 fn io_errors_convert_via_from() {
216 let io = std::io::Error::new(std::io::ErrorKind::BrokenPipe, "pipe");
217 let e: StreamError = io.into();
218 assert!(matches!(e, StreamError::Io(_)));
219 }
220
221 #[test]
222 fn structured_variants_carry_context() {
223 let e = StreamError::StreamNotFound {
224 app: "live".into(),
225 stream_id: "cam".into(),
226 };
227 assert_eq!(
228 e.to_string(),
229 "Stream 'cam' not found in application 'live'"
230 );
231 }
232}