use std::io;
use std::net::SocketAddr;
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum Error {
#[error("I/O error: {0}")]
Io(#[from] io::Error),
#[error("Network error: {message}")]
Network {
message: String,
#[source]
source: Option<io::Error>,
},
#[error("Failed to connect/bind to {addr}: {source}")]
Connection {
addr: SocketAddr,
#[source]
source: io::Error,
},
#[error("Serialization error: {0}")]
Serialization(#[from] bincode::error::EncodeError),
#[error("Deserialization failed: {0}")]
Deserialization(String),
#[error("Invalid configuration: {0}")]
Config(String),
#[error("Peer not found: {0}")]
PeerNotFound(SocketAddr),
#[error("Message size {size} exceeds maximum {max}")]
MessageTooLarge {
size: usize,
max: usize,
},
#[error("Channel send/receive error: {0}")]
Channel(String),
#[cfg(feature = "crypto")]
#[error("Cryptographic error: {0}")]
Crypto(String),
#[cfg(feature = "crypto")]
#[error("Invalid signature from peer {0}")]
InvalidSignature(SocketAddr),
#[error("Internal error: {0}")]
Internal(String),
}
impl Error {
pub fn network(message: impl Into<String>) -> Self {
Self::Network {
message: message.into(),
source: None,
}
}
pub fn network_with_source(message: impl Into<String>, source: io::Error) -> Self {
Self::Network {
message: message.into(),
source: Some(source),
}
}
pub fn internal(message: impl Into<String>) -> Self {
Self::Internal(message.into())
}
}
#[macro_export]
macro_rules! internal_error {
($msg:expr) => {
$crate::error::Error::internal(format!("{} at {}:{}", $msg, file!(), line!()))
};
($fmt:expr, $($arg:tt)*) => {
$crate::error::Error::internal(format!("{} at {}:{}", format!($fmt, $($arg)*), file!(), line!()))
};
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Result;
#[test]
fn error_from_bincode() {
let bad_data: &[u8] = &[255, 255, 255];
let result: Result<(u32, usize)> =
bincode::serde::decode_from_slice(bad_data, bincode::config::standard())
.map_err(|e| Error::Deserialization(e.to_string()));
assert!(result.is_err());
assert!(matches!(result.unwrap_err(), Error::Deserialization(_)));
}
#[test]
fn internal_error_macro() {
let err = internal_error!("test error");
assert!(matches!(err, Error::Internal(_)));
let msg = format!("{}", err);
assert!(msg.contains("test error"));
assert!(msg.contains("error.rs")); }
#[test]
fn internal_error_macro_with_format() {
let value = 42;
let err = internal_error!("value is {}", value);
assert!(matches!(err, Error::Internal(_)));
let msg = format!("{err}");
assert!(msg.contains("value is 42"));
assert!(msg.contains("error.rs"));
}
#[test]
fn error_source_chain() {
use std::error::Error as StdError;
let io_err = io::Error::other("inner error");
let err = Error::network_with_source("outer error", io_err);
assert!(err.source().is_some());
}
}