use std::borrow::Cow;
use std::io;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum Error {
#[error("network interface {0:?} not found")]
InterfaceNotFound(String),
#[error("failed to open packet socket: {0}")]
Socket(#[source] io::Error),
#[error("failed to bind packet socket to interface: {0}")]
Bind(#[source] io::Error),
#[error("failed to set socket option {option}: {source}")]
SetSockOpt {
option: &'static str,
#[source]
source: io::Error,
},
#[error("failed to mmap packet ring: {0}")]
Mmap(#[source] io::Error),
#[error("frame of {len} bytes exceeds maximum frame size of {max} bytes")]
FrameTooLarge {
len: usize,
max: usize,
},
#[error("invalid channel configuration: {0}")]
InvalidConfig(Cow<'static, str>),
#[error("failed to send frame: {0}")]
Send(#[source] io::Error),
#[error("failed to receive: {0}")]
Recv(#[source] io::Error),
#[error("failed to read channel statistics: {0}")]
Stats(#[source] io::Error),
#[error("failed to look up network interfaces: {0}")]
Interfaces(#[source] io::Error),
#[cfg(feature = "tokio")]
#[error("failed to register with the async reactor: {0}")]
Register(#[source] io::Error),
}
impl Error {
#[must_use]
pub fn as_io(&self) -> Option<&io::Error> {
match self {
Error::Socket(e)
| Error::Bind(e)
| Error::SetSockOpt { source: e, .. }
| Error::Mmap(e)
| Error::Send(e)
| Error::Recv(e)
| Error::Stats(e)
| Error::Interfaces(e) => Some(e),
#[cfg(feature = "tokio")]
Error::Register(e) => Some(e),
Error::InterfaceNotFound(_) | Error::FrameTooLarge { .. } | Error::InvalidConfig(_) => {
None
}
}
}
pub(crate) fn invalid_config(msg: impl Into<Cow<'static, str>>) -> Self {
Error::InvalidConfig(msg.into())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn as_io_reaches_the_os_error_across_variants() {
let e = Error::Recv(io::Error::from(io::ErrorKind::UnexpectedEof));
assert_eq!(e.as_io().unwrap().kind(), io::ErrorKind::UnexpectedEof);
let e = Error::Send(io::Error::from(io::ErrorKind::WouldBlock));
assert_eq!(e.as_io().unwrap().kind(), io::ErrorKind::WouldBlock);
let e = Error::SetSockOpt {
option: "PACKET_FANOUT",
source: io::Error::from_raw_os_error(libc::EINVAL),
};
assert_eq!(e.as_io().unwrap().raw_os_error(), Some(libc::EINVAL));
assert!(Error::invalid_config("nope").as_io().is_none());
assert!(Error::FrameTooLarge { len: 1, max: 0 }.as_io().is_none());
}
#[test]
fn display_names_the_operation() {
let e = Error::Send(io::Error::from(io::ErrorKind::WouldBlock));
assert!(e.to_string().starts_with("failed to send frame"), "{e}");
let e = Error::Stats(io::Error::from(io::ErrorKind::PermissionDenied));
assert!(e.to_string().starts_with("failed to read channel statistics"), "{e}");
}
}