Skip to main content

oximedia_net/
error.rs

1//! Error types for network streaming operations.
2//!
3//! This module provides the [`NetError`] type which represents all errors
4//! that can occur during network streaming operations.
5
6use std::io;
7use thiserror::Error;
8
9/// Error type for network streaming operations.
10///
11/// This enum covers all possible errors that can occur during network
12/// streaming, including connection errors, protocol errors, and timeouts.
13#[derive(Debug, Error)]
14pub enum NetError {
15    /// I/O error during network operations.
16    #[error("I/O error: {0}")]
17    Io(#[from] io::Error),
18
19    /// Connection error.
20    #[error("Connection error: {0}")]
21    Connection(String),
22
23    /// Protocol error with description.
24    #[error("Protocol error: {0}")]
25    Protocol(String),
26
27    /// Timeout during network operation.
28    #[error("Timeout: {0}")]
29    Timeout(String),
30
31    /// Invalid URL format.
32    #[error("Invalid URL: {0}")]
33    InvalidUrl(String),
34
35    /// HTTP error with status code.
36    #[error("HTTP error: status {status}, {message}")]
37    Http {
38        /// HTTP status code.
39        status: u16,
40        /// Error message.
41        message: String,
42    },
43
44    /// Parse error in protocol data.
45    #[error("Parse error at offset {offset}: {message}")]
46    Parse {
47        /// Byte offset where the error occurred.
48        offset: u64,
49        /// Description of the parse error.
50        message: String,
51    },
52
53    /// Invalid state for the requested operation.
54    #[error("Invalid state: {0}")]
55    InvalidState(String),
56
57    /// Handshake failed.
58    #[error("Handshake failed: {0}")]
59    Handshake(String),
60
61    /// Authentication failed.
62    #[error("Authentication failed: {0}")]
63    Authentication(String),
64
65    /// Resource not found.
66    #[error("Not found: {0}")]
67    NotFound(String),
68
69    /// Segment error in streaming protocols.
70    #[error("Segment error: {0}")]
71    Segment(String),
72
73    /// Playlist error in HLS/DASH.
74    #[error("Playlist error: {0}")]
75    Playlist(String),
76
77    /// Encoding/decoding error.
78    #[error("Encoding error: {0}")]
79    Encoding(String),
80
81    /// Buffer overflow or underflow.
82    #[error("Buffer error: {0}")]
83    Buffer(String),
84
85    /// End of stream reached.
86    #[error("End of stream")]
87    Eof,
88
89    /// Core library error.
90    #[error("Core error: {0}")]
91    Core(#[from] oximedia_core::OxiError),
92}
93
94impl NetError {
95    /// Creates a new connection error.
96    #[must_use]
97    pub fn connection(message: impl Into<String>) -> Self {
98        Self::Connection(message.into())
99    }
100
101    /// Creates a new protocol error.
102    #[must_use]
103    pub fn protocol(message: impl Into<String>) -> Self {
104        Self::Protocol(message.into())
105    }
106
107    /// Creates a new timeout error.
108    #[must_use]
109    pub fn timeout(message: impl Into<String>) -> Self {
110        Self::Timeout(message.into())
111    }
112
113    /// Creates a new invalid URL error.
114    #[must_use]
115    pub fn invalid_url(message: impl Into<String>) -> Self {
116        Self::InvalidUrl(message.into())
117    }
118
119    /// Creates a new HTTP error.
120    #[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    /// Creates a new parse error.
129    #[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    /// Creates a new invalid state error.
138    #[must_use]
139    pub fn invalid_state(message: impl Into<String>) -> Self {
140        Self::InvalidState(message.into())
141    }
142
143    /// Creates a new handshake error.
144    #[must_use]
145    pub fn handshake(message: impl Into<String>) -> Self {
146        Self::Handshake(message.into())
147    }
148
149    /// Creates a new authentication error.
150    #[must_use]
151    pub fn authentication(message: impl Into<String>) -> Self {
152        Self::Authentication(message.into())
153    }
154
155    /// Creates a new not found error.
156    #[must_use]
157    pub fn not_found(message: impl Into<String>) -> Self {
158        Self::NotFound(message.into())
159    }
160
161    /// Creates a new segment error.
162    #[must_use]
163    pub fn segment(message: impl Into<String>) -> Self {
164        Self::Segment(message.into())
165    }
166
167    /// Creates a new playlist error.
168    #[must_use]
169    pub fn playlist(message: impl Into<String>) -> Self {
170        Self::Playlist(message.into())
171    }
172
173    /// Creates a new encoding error.
174    #[must_use]
175    pub fn encoding(message: impl Into<String>) -> Self {
176        Self::Encoding(message.into())
177    }
178
179    /// Creates a new buffer error.
180    #[must_use]
181    pub fn buffer(message: impl Into<String>) -> Self {
182        Self::Buffer(message.into())
183    }
184
185    /// Returns true if this is an end-of-stream error.
186    #[must_use]
187    pub const fn is_eof(&self) -> bool {
188        matches!(self, Self::Eof)
189    }
190
191    /// Returns true if this is a timeout error.
192    #[must_use]
193    pub const fn is_timeout(&self) -> bool {
194        matches!(self, Self::Timeout(_))
195    }
196
197    /// Returns true if this is a connection error.
198    #[must_use]
199    pub const fn is_connection(&self) -> bool {
200        matches!(self, Self::Connection(_))
201    }
202}
203
204/// Result type alias for network streaming operations.
205pub 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}