use arcbox_error::CommonError;
use thiserror::Error;
pub type Result<T> = std::result::Result<T, NetError>;
#[derive(Debug, Error)]
pub enum NetError {
#[error(transparent)]
Common(#[from] CommonError),
#[error("interface error: {0}")]
Interface(String),
#[error("address allocation error: {0}")]
AddressAllocation(String),
#[error("port forwarding error: {0}")]
PortForward(String),
#[error("DNS error: {0}")]
Dns(String),
#[error("backend error: {0}")]
Backend(String),
#[error("netlink error: {0}")]
Netlink(String),
#[error("bridge error: {0}")]
Bridge(String),
#[error("TAP error: {0}")]
Tap(String),
#[error("firewall error: {0}")]
Firewall(String),
#[error("NAT error: {0}")]
Nat(String),
#[error("datapath error: {0}")]
Datapath(String),
#[error("ring buffer error: {0}")]
RingBuffer(String),
#[error("packet pool error: {0}")]
PacketPool(String),
#[error("connection tracking error: {0}")]
ConnTrack(String),
#[error("checksum error: {0}")]
Checksum(String),
#[error("mDNS error: {0}")]
Mdns(String),
}
impl From<std::io::Error> for NetError {
fn from(err: std::io::Error) -> Self {
Self::Common(CommonError::from(err))
}
}
impl From<arcbox_datapath::Error> for NetError {
fn from(err: arcbox_datapath::Error) -> Self {
match err {
arcbox_datapath::Error::PacketPool(msg) => Self::PacketPool(msg),
}
}
}
#[cfg(target_os = "macos")]
impl From<arcbox_vmnet::VmnetError> for NetError {
fn from(err: arcbox_vmnet::VmnetError) -> Self {
match err {
arcbox_vmnet::VmnetError::Config(msg) => {
Self::Common(CommonError::config(format!("vmnet: {msg}")))
}
arcbox_vmnet::VmnetError::Io(io) => Self::Common(CommonError::from(io)),
}
}
}
impl NetError {
#[must_use]
pub fn config(msg: impl Into<String>) -> Self {
Self::Common(CommonError::config(msg))
}
#[must_use]
pub fn io(err: std::io::Error) -> Self {
Self::Common(CommonError::from(err))
}
}
#[cfg(all(test, target_os = "macos"))]
mod tests {
use super::*;
#[test]
fn vmnet_config_error_keeps_origin_prefix() {
let err: NetError =
arcbox_vmnet::VmnetError::config("bridge mode requires interface name").into();
let s = err.to_string();
assert!(
s.contains("vmnet: bridge mode requires interface name"),
"missing origin prefix: {s}"
);
}
#[test]
fn vmnet_io_error_passes_through() {
let io = std::io::Error::new(std::io::ErrorKind::PermissionDenied, "denied");
let err: NetError = arcbox_vmnet::VmnetError::Io(io).into();
match err {
NetError::Common(common) if common.is_io() => {}
other => panic!("expected Common(io), got {other:?}"),
}
}
}