use std::fmt;
#[derive(Debug, Clone)]
pub struct TypeError {
message: String,
}
impl TypeError {
pub fn new(message: &str) -> Self {
Self {
message: message.to_string(),
}
}
pub fn message(&self) -> &str {
&self.message
}
}
impl fmt::Display for TypeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "TypeError: {}", self.message)
}
}
impl std::error::Error for TypeError {}
#[derive(Debug, Clone)]
pub struct NetworkError {
message: String,
}
impl NetworkError {
pub fn new(message: &str) -> Self {
Self {
message: message.to_string(),
}
}
pub fn message(&self) -> &str {
&self.message
}
}
impl fmt::Display for NetworkError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "NetworkError: {}", self.message)
}
}
impl std::error::Error for NetworkError {}
#[derive(Debug, Clone)]
pub struct AbortError {
message: String,
}
impl AbortError {
pub fn new(message: &str) -> Self {
Self {
message: message.to_string(),
}
}
pub fn message(&self) -> &str {
&self.message
}
}
impl fmt::Display for AbortError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "AbortError: {}", self.message)
}
}
impl std::error::Error for AbortError {}
#[derive(Debug)]
pub enum FetchError {
Type(TypeError),
Network(NetworkError),
Abort(AbortError),
}
impl fmt::Display for FetchError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Type(e) => write!(f, "{}", e),
Self::Network(e) => write!(f, "{}", e),
Self::Abort(e) => write!(f, "{}", e),
}
}
}
impl std::error::Error for FetchError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Type(e) => Some(e),
Self::Network(e) => Some(e),
Self::Abort(e) => Some(e),
}
}
}
impl From<TypeError> for FetchError {
fn from(err: TypeError) -> Self {
Self::Type(err)
}
}
impl From<NetworkError> for FetchError {
fn from(err: NetworkError) -> Self {
Self::Network(err)
}
}
impl From<AbortError> for FetchError {
fn from(err: AbortError) -> Self {
Self::Abort(err)
}
}
impl From<hyper::Error> for FetchError {
fn from(err: hyper::Error) -> Self {
Self::Network(NetworkError::new(&err.to_string()))
}
}
impl From<hyper_util::client::legacy::Error> for FetchError {
fn from(err: hyper_util::client::legacy::Error) -> Self {
Self::Network(NetworkError::new(&err.to_string()))
}
}
impl From<http::Error> for FetchError {
fn from(_: http::Error) -> Self {
Self::Network(NetworkError::new("HTTP error"))
}
}
impl From<url::ParseError> for FetchError {
fn from(_: url::ParseError) -> Self {
Self::Type(TypeError::new("Invalid URL"))
}
}
impl From<serde_json::Error> for FetchError {
fn from(_: serde_json::Error) -> Self {
Self::Type(TypeError::new("JSON parse error"))
}
}
pub type Result<T> = std::result::Result<T, FetchError>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_display() {
let type_error = TypeError::new("test message");
assert_eq!(format!("{}", type_error), "TypeError: test message");
let network_error = NetworkError::new("connection failed");
assert_eq!(
format!("{}", network_error),
"NetworkError: connection failed"
);
let abort_error = AbortError::new("aborted");
assert_eq!(format!("{}", abort_error), "AbortError: aborted");
}
#[test]
fn test_fetch_error_conversions() {
let type_error = TypeError::new("test");
let fetch_error: FetchError = type_error.into();
assert!(matches!(fetch_error, FetchError::Type(_)));
let network_error = NetworkError::new("test");
let fetch_error: FetchError = network_error.into();
assert!(matches!(fetch_error, FetchError::Network(_)));
let abort_error = AbortError::new("test");
let fetch_error: FetchError = abort_error.into();
assert!(matches!(fetch_error, FetchError::Abort(_)));
}
#[test]
fn test_error_messages() {
let type_error = TypeError::new("invalid input");
assert_eq!(type_error.message(), "invalid input");
let network_error = NetworkError::new("timeout");
assert_eq!(network_error.message(), "timeout");
let abort_error = AbortError::new("cancelled");
assert_eq!(abort_error.message(), "cancelled");
}
}