1use std::io;
7use thiserror::Error;
8
9#[derive(Debug, Error)]
14pub enum NetError {
15 #[error("I/O error: {0}")]
17 Io(#[from] io::Error),
18
19 #[error("Connection error: {0}")]
21 Connection(String),
22
23 #[error("Protocol error: {0}")]
25 Protocol(String),
26
27 #[error("Timeout: {0}")]
29 Timeout(String),
30
31 #[error("Invalid URL: {0}")]
33 InvalidUrl(String),
34
35 #[error("HTTP error: status {status}, {message}")]
37 Http {
38 status: u16,
40 message: String,
42 },
43
44 #[error("Parse error at offset {offset}: {message}")]
46 Parse {
47 offset: u64,
49 message: String,
51 },
52
53 #[error("Invalid state: {0}")]
55 InvalidState(String),
56
57 #[error("Handshake failed: {0}")]
59 Handshake(String),
60
61 #[error("Authentication failed: {0}")]
63 Authentication(String),
64
65 #[error("Not found: {0}")]
67 NotFound(String),
68
69 #[error("Segment error: {0}")]
71 Segment(String),
72
73 #[error("Playlist error: {0}")]
75 Playlist(String),
76
77 #[error("Encoding error: {0}")]
79 Encoding(String),
80
81 #[error("Buffer error: {0}")]
83 Buffer(String),
84
85 #[error("End of stream")]
87 Eof,
88
89 #[error("Core error: {0}")]
91 Core(#[from] oximedia_core::OxiError),
92}
93
94impl NetError {
95 #[must_use]
97 pub fn connection(message: impl Into<String>) -> Self {
98 Self::Connection(message.into())
99 }
100
101 #[must_use]
103 pub fn protocol(message: impl Into<String>) -> Self {
104 Self::Protocol(message.into())
105 }
106
107 #[must_use]
109 pub fn timeout(message: impl Into<String>) -> Self {
110 Self::Timeout(message.into())
111 }
112
113 #[must_use]
115 pub fn invalid_url(message: impl Into<String>) -> Self {
116 Self::InvalidUrl(message.into())
117 }
118
119 #[must_use]
121 pub fn http(status: u16, message: impl Into<String>) -> Self {
122 Self::Http {
123 status,
124 message: message.into(),
125 }
126 }
127
128 #[must_use]
130 pub fn parse(offset: u64, message: impl Into<String>) -> Self {
131 Self::Parse {
132 offset,
133 message: message.into(),
134 }
135 }
136
137 #[must_use]
139 pub fn invalid_state(message: impl Into<String>) -> Self {
140 Self::InvalidState(message.into())
141 }
142
143 #[must_use]
145 pub fn handshake(message: impl Into<String>) -> Self {
146 Self::Handshake(message.into())
147 }
148
149 #[must_use]
151 pub fn authentication(message: impl Into<String>) -> Self {
152 Self::Authentication(message.into())
153 }
154
155 #[must_use]
157 pub fn not_found(message: impl Into<String>) -> Self {
158 Self::NotFound(message.into())
159 }
160
161 #[must_use]
163 pub fn segment(message: impl Into<String>) -> Self {
164 Self::Segment(message.into())
165 }
166
167 #[must_use]
169 pub fn playlist(message: impl Into<String>) -> Self {
170 Self::Playlist(message.into())
171 }
172
173 #[must_use]
175 pub fn encoding(message: impl Into<String>) -> Self {
176 Self::Encoding(message.into())
177 }
178
179 #[must_use]
181 pub fn buffer(message: impl Into<String>) -> Self {
182 Self::Buffer(message.into())
183 }
184
185 #[must_use]
187 pub const fn is_eof(&self) -> bool {
188 matches!(self, Self::Eof)
189 }
190
191 #[must_use]
193 pub const fn is_timeout(&self) -> bool {
194 matches!(self, Self::Timeout(_))
195 }
196
197 #[must_use]
199 pub const fn is_connection(&self) -> bool {
200 matches!(self, Self::Connection(_))
201 }
202}
203
204pub type NetResult<T> = Result<T, NetError>;
206
207#[cfg(test)]
208mod tests {
209 use super::*;
210
211 #[test]
212 fn test_connection_error() {
213 let err = NetError::connection("Failed to connect");
214 assert!(err.is_connection());
215 assert!(format!("{err}").contains("Failed to connect"));
216 }
217
218 #[test]
219 fn test_protocol_error() {
220 let err = NetError::protocol("Invalid message format");
221 assert!(format!("{err}").contains("Invalid message format"));
222 }
223
224 #[test]
225 fn test_timeout_error() {
226 let err = NetError::timeout("Connection timed out");
227 assert!(err.is_timeout());
228 assert!(format!("{err}").contains("Connection timed out"));
229 }
230
231 #[test]
232 fn test_http_error() {
233 let err = NetError::http(404, "Not Found");
234 assert!(format!("{err}").contains("404"));
235 assert!(format!("{err}").contains("Not Found"));
236 }
237
238 #[test]
239 fn test_parse_error() {
240 let err = NetError::parse(100, "Invalid header");
241 if let NetError::Parse { offset, message } = err {
242 assert_eq!(offset, 100);
243 assert_eq!(message, "Invalid header");
244 } else {
245 panic!("Expected Parse error");
246 }
247 }
248
249 #[test]
250 fn test_eof_error() {
251 let err = NetError::Eof;
252 assert!(err.is_eof());
253 }
254
255 #[test]
256 fn test_io_error_from() {
257 let io_err = io::Error::new(io::ErrorKind::NotFound, "file not found");
258 let err: NetError = io_err.into();
259 assert!(matches!(err, NetError::Io(_)));
260 }
261}