use std::io;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum NetError {
#[error("I/O error: {0}")]
Io(#[from] io::Error),
#[error("Connection error: {0}")]
Connection(String),
#[error("Protocol error: {0}")]
Protocol(String),
#[error("Timeout: {0}")]
Timeout(String),
#[error("Invalid URL: {0}")]
InvalidUrl(String),
#[error("HTTP error: status {status}, {message}")]
Http {
status: u16,
message: String,
},
#[error("Parse error at offset {offset}: {message}")]
Parse {
offset: u64,
message: String,
},
#[error("Invalid state: {0}")]
InvalidState(String),
#[error("Handshake failed: {0}")]
Handshake(String),
#[error("Authentication failed: {0}")]
Authentication(String),
#[error("Not found: {0}")]
NotFound(String),
#[error("Segment error: {0}")]
Segment(String),
#[error("Playlist error: {0}")]
Playlist(String),
#[error("Encoding error: {0}")]
Encoding(String),
#[error("Buffer error: {0}")]
Buffer(String),
#[error("End of stream")]
Eof,
#[error("Core error: {0}")]
Core(#[from] oximedia_core::OxiError),
}
impl NetError {
#[must_use]
pub fn connection(message: impl Into<String>) -> Self {
Self::Connection(message.into())
}
#[must_use]
pub fn protocol(message: impl Into<String>) -> Self {
Self::Protocol(message.into())
}
#[must_use]
pub fn timeout(message: impl Into<String>) -> Self {
Self::Timeout(message.into())
}
#[must_use]
pub fn invalid_url(message: impl Into<String>) -> Self {
Self::InvalidUrl(message.into())
}
#[must_use]
pub fn http(status: u16, message: impl Into<String>) -> Self {
Self::Http {
status,
message: message.into(),
}
}
#[must_use]
pub fn parse(offset: u64, message: impl Into<String>) -> Self {
Self::Parse {
offset,
message: message.into(),
}
}
#[must_use]
pub fn invalid_state(message: impl Into<String>) -> Self {
Self::InvalidState(message.into())
}
#[must_use]
pub fn handshake(message: impl Into<String>) -> Self {
Self::Handshake(message.into())
}
#[must_use]
pub fn authentication(message: impl Into<String>) -> Self {
Self::Authentication(message.into())
}
#[must_use]
pub fn not_found(message: impl Into<String>) -> Self {
Self::NotFound(message.into())
}
#[must_use]
pub fn segment(message: impl Into<String>) -> Self {
Self::Segment(message.into())
}
#[must_use]
pub fn playlist(message: impl Into<String>) -> Self {
Self::Playlist(message.into())
}
#[must_use]
pub fn encoding(message: impl Into<String>) -> Self {
Self::Encoding(message.into())
}
#[must_use]
pub fn buffer(message: impl Into<String>) -> Self {
Self::Buffer(message.into())
}
#[must_use]
pub const fn is_eof(&self) -> bool {
matches!(self, Self::Eof)
}
#[must_use]
pub const fn is_timeout(&self) -> bool {
matches!(self, Self::Timeout(_))
}
#[must_use]
pub const fn is_connection(&self) -> bool {
matches!(self, Self::Connection(_))
}
}
pub type NetResult<T> = Result<T, NetError>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_connection_error() {
let err = NetError::connection("Failed to connect");
assert!(err.is_connection());
assert!(format!("{err}").contains("Failed to connect"));
}
#[test]
fn test_protocol_error() {
let err = NetError::protocol("Invalid message format");
assert!(format!("{err}").contains("Invalid message format"));
}
#[test]
fn test_timeout_error() {
let err = NetError::timeout("Connection timed out");
assert!(err.is_timeout());
assert!(format!("{err}").contains("Connection timed out"));
}
#[test]
fn test_http_error() {
let err = NetError::http(404, "Not Found");
assert!(format!("{err}").contains("404"));
assert!(format!("{err}").contains("Not Found"));
}
#[test]
fn test_parse_error() {
let err = NetError::parse(100, "Invalid header");
if let NetError::Parse { offset, message } = err {
assert_eq!(offset, 100);
assert_eq!(message, "Invalid header");
} else {
panic!("Expected Parse error");
}
}
#[test]
fn test_eof_error() {
let err = NetError::Eof;
assert!(err.is_eof());
}
#[test]
fn test_io_error_from() {
let io_err = io::Error::new(io::ErrorKind::NotFound, "file not found");
let err: NetError = io_err.into();
assert!(matches!(err, NetError::Io(_)));
}
}