use crate::{
fsemul::errors::{FSEmulAPIError, FSEmulFSError, FSEmulNetworkError, FSEmulProtocolError},
mion::errors::{MionAPIError, MionProtocolError},
};
use bytes::Bytes;
use miette::{Diagnostic, Report};
use std::{ffi::FromBytesUntilNulError, str::Utf8Error, string::FromUtf8Error, time::Duration};
use thiserror::Error;
use tokio::{io::Error as IoError, task::JoinError};
use walkdir::Error as WalkdirError;
#[cfg(feature = "clients")]
use crate::net::client::errors::CommonNetClientNetworkError;
#[cfg(feature = "clients")]
use local_ip_address::Error as LocalIpAddressError;
#[cfg(feature = "clients")]
use network_interface::Error as NetworkInterfaceError;
#[cfg(feature = "clients")]
use reqwest::Error as ReqwestError;
#[cfg(any(feature = "clients", feature = "servers"))]
use crate::net::errors::{CommonNetAPIError, CommonNetNetworkError};
#[cfg(feature = "servers")]
use crate::net::server::models::ResponseStreamMessage;
#[cfg(feature = "servers")]
use tokio::sync::mpsc::error::SendError;
#[derive(Error, Diagnostic, Debug)]
pub enum CatBridgeError {
#[error(transparent)]
#[diagnostic(transparent)]
API(#[from] APIError),
#[error(
"We could not send a message locally to another part of the process. This channel must've been closed unexpectedly."
)]
#[diagnostic(code(cat_dev::closed_channel))]
ClosedChannel,
#[error(transparent)]
#[diagnostic(transparent)]
FS(#[from] FSError),
#[error("We could not await an asynchronous task we spawned: {0:?}")]
#[diagnostic(code(cat_dev::join_failure))]
JoinFailure(#[from] JoinError),
#[error(transparent)]
#[diagnostic(transparent)]
Network(#[from] NetworkError),
#[error("We could not spawn a task (a lightweight thread) to do work on.")]
#[diagnostic(code(cat_dev::spawn_failure))]
SpawnFailure(IoError),
#[error("An unknown error we couldn't pin down occured: {0:?}")]
#[diagnostic(code(cat_dev::unknown))]
UnknownError(Report),
#[error(
"This cat-dev API requires a 32 bit usize, and this machine does not have it, please upgrade your machine."
)]
#[diagnostic(code(cat_dev::unsupported_bits_per_core))]
UnsupportedBitsPerCore,
}
#[non_exhaustive]
#[derive(Error, Diagnostic, Debug)]
pub enum APIError {
#[cfg_attr(docsrs, doc(cfg(any(feature = "clients", feature = "servers"))))]
#[cfg(any(feature = "clients", feature = "servers"))]
#[error(transparent)]
#[diagnostic(transparent)]
CommonNet(#[from] CommonNetAPIError),
#[error(transparent)]
#[diagnostic(transparent)]
FSEmul(#[from] FSEmulAPIError),
#[error(transparent)]
#[diagnostic(transparent)]
Mion(#[from] MionAPIError),
#[error(
"We could not find the local hosts ipv4 address which is needed if an ip isn't explicitly passed in."
)]
#[diagnostic(code(cat_dev::api::no_host_ip_found))]
NoHostIpFound,
}
#[cfg_attr(docsrs, doc(cfg(any(feature = "clients", feature = "servers"))))]
#[cfg(any(feature = "clients", feature = "servers"))]
impl From<CommonNetAPIError> for CatBridgeError {
fn from(value: CommonNetAPIError) -> Self {
Self::from(APIError::CommonNet(value))
}
}
#[derive(Error, Diagnostic, Debug)]
pub enum FSError {
#[error(
"We can't find the path to store a complete list of host-bridges, please use explicit paths instead."
)]
#[diagnostic(code(cat_dev::fs::cant_find_hostenv_path))]
CantFindHostEnvPath,
#[error(transparent)]
#[diagnostic(transparent)]
FSEmul(#[from] FSEmulFSError),
#[error("Data read from the filesystem was expected to be a valid INI file: {0}")]
#[diagnostic(code(cat_dev::fs::expected_ini))]
InvalidDataNeedsToBeINI(String),
#[error("Expected file magic of: {0}, got {1} as magic bytes")]
#[diagnostic(code(cat_dev::fs::invalid_file_magic))]
InvalidFileMagic(u32, u32),
#[error("Expected file size of: {0} bytes, got a file sized {1} bytes")]
#[diagnostic(code(cat_dev::fs::invalid_file_size))]
InvalidFileSize(usize, usize),
#[error("Error writing/reading data from the filesystem: {0}")]
#[diagnostic(code(cat_dev::fs::io))]
IO(#[from] IoError),
#[error("Error iterating through folder: {0:?}")]
#[diagnostic(code(cat_dev::fs::iterating_folder_error))]
IteratingFolderError(#[from] WalkdirError),
#[error("Expect file to have at least: {0} line(s), but it was only: {1} line(s) long.")]
#[diagnostic(code(cat_dev::fs::too_few_lines))]
TooFewLines(usize, usize),
#[error("File cannot be larger than: {0} bytes, is {1} bytes")]
#[diagnostic(code(cat_dev::fs::too_large))]
TooLarge(usize, usize),
#[error("File needs to be at least: {0} bytes, is {1} bytes")]
#[diagnostic(code(cat_dev::fs::too_small))]
TooSmall(usize, usize),
#[error("Data read from the filesystem was expected to be UTF-8, but was not: {0}")]
#[diagnostic(code(cat_dev::fs::utf8_expected))]
Utf8Expected(#[from] FromUtf8Error),
}
#[derive(Error, Diagnostic, Debug)]
#[non_exhaustive]
pub enum NetworkError {
#[error("Failed to bind to a local address to receive packets.")]
#[diagnostic(code(cat_dev::net::bind_failure))]
BindFailure,
#[cfg_attr(docsrs, doc(cfg(feature = "clients")))]
#[cfg(feature = "clients")]
#[error(transparent)]
#[diagnostic(transparent)]
CommonClient(#[from] CommonNetClientNetworkError),
#[cfg_attr(docsrs, doc(cfg(any(feature = "clients", feature = "servers"))))]
#[cfg(any(feature = "clients", feature = "servers"))]
#[error(transparent)]
#[diagnostic(transparent)]
CommonNet(#[from] CommonNetNetworkError),
#[error("Expected some sort of data from other side, but got none.")]
#[diagnostic(code(cat_dev::net::expected_data))]
ExpectedData,
#[error(transparent)]
#[diagnostic(transparent)]
FSEmul(#[from] FSEmulNetworkError),
#[cfg_attr(docsrs, doc(cfg(feature = "clients")))]
#[cfg(feature = "clients")]
#[error("Underlying HTTP client error: {0}")]
#[diagnostic(code(cat_dev::net::http_failure))]
HTTP(#[from] ReqwestError),
#[error("Error talking to the network could not send/receive data: {0}")]
#[diagnostic(code(cat_dev::net::io_error))]
IO(#[from] IoError),
#[cfg_attr(docsrs, doc(cfg(feature = "clients")))]
#[cfg(feature = "clients")]
#[error("Failed to list the network interfaces on your device: {0:?}.")]
#[diagnostic(code(cat_dev::net::list_interfaces_error))]
ListInterfacesFailure(NetworkInterfaceError),
#[cfg_attr(docsrs, doc(cfg(feature = "clients")))]
#[cfg(feature = "clients")]
#[error("Failure fetching local ip address: {0}")]
#[diagnostic(code(cat_dev::net::local_ip_failure))]
LocalIp(#[from] LocalIpAddressError),
#[error(transparent)]
#[diagnostic(transparent)]
Parse(#[from] NetworkParseError),
#[error(
"Failed to set the socket we're bound on as a broadcast address, this is needed to discover CAT devices."
)]
#[diagnostic(code(cat_dev::net::set_broadcast_failure))]
SetBroadcastFailure,
#[cfg_attr(docsrs, doc(cfg(feature = "servers")))]
#[cfg(feature = "servers")]
#[error("Error queueing up packet to be sent out over a conenction: {0:?}")]
#[diagnostic(code(cat_dev::net::send_queue_failure))]
SendQueueMessageFailure(#[from] SendError<ResponseStreamMessage>),
#[error(
"Timed out while writing/reading data from the network, failed to send and receive data."
)]
#[diagnostic(code(cat_dev::net::timeout))]
Timeout(Duration),
}
#[cfg_attr(docsrs, doc(cfg(feature = "clients")))]
#[cfg(feature = "clients")]
impl From<CommonNetClientNetworkError> for CatBridgeError {
fn from(value: CommonNetClientNetworkError) -> Self {
Self::Network(value.into())
}
}
#[cfg_attr(docsrs, doc(cfg(any(feature = "clients", feature = "servers"))))]
#[cfg(any(feature = "clients", feature = "servers"))]
impl From<CommonNetNetworkError> for CatBridgeError {
fn from(value: CommonNetNetworkError) -> Self {
Self::Network(value.into())
}
}
#[cfg_attr(docsrs, doc(cfg(feature = "clients")))]
#[cfg(feature = "clients")]
impl From<ReqwestError> for CatBridgeError {
fn from(value: ReqwestError) -> Self {
Self::Network(value.into())
}
}
#[derive(Error, Diagnostic, Debug, PartialEq, Eq)]
pub enum NetworkParseError {
#[error("Failed reading c style string from packet: {0:?}")]
#[diagnostic(code(cat_dev::net::parse::bad_c_string))]
BadCString(#[from] FromBytesUntilNulError),
#[error(
"Tried to read Packet of type ({0}) from network, must be encoded exactly as [{1:02x?}], but got [{2:02x?}]"
)]
#[diagnostic(code(cat_dev::net::parse::doesnt_match_static_data))]
DoesntMatchStaticPayload(&'static str, &'static [u8], Bytes),
#[error("Internal Protocol responded with an error code: {0}")]
#[diagnostic(code(cat_dev::net::parse::error_code))]
ErrorCode(u32),
#[error("Reading Field {1} from Packet {0}, was not encoded correctly must be encoded as {2}")]
#[diagnostic(code(cat_dev::net::parse::field_encoded_incorrectly))]
FieldEncodedIncorrectly(&'static str, &'static str, &'static str),
#[error(
"Tried Reading Field {1} from Packet {0}. This Field requires at least {2} bytes, but only had {3}, bytes: {4:02x?}"
)]
#[diagnostic(code(cat_dev::net::parse::field_not_long_enough))]
FieldNotLongEnough(&'static str, &'static str, usize, usize, Bytes),
#[error(transparent)]
#[diagnostic(transparent)]
FSEmul(#[from] FSEmulProtocolError),
#[error(transparent)]
#[diagnostic(transparent)]
Mion(#[from] MionProtocolError),
#[error(
"Tried to read Packet of type ({0}) from network needs at least {1} bytes, but only got {2} bytes: {3:02x?}"
)]
#[diagnostic(code(cat_dev::net::parse::not_enough_data))]
NotEnoughData(&'static str, usize, usize, Bytes),
#[error(
"Unexpected Trailer for Packet `{0}` received from the network (we're not sure what do with this extra data), extra bytes: {1:02x?}"
)]
#[diagnostic(code(cat_dev::net::parse::unexpected_trailer))]
UnexpectedTrailer(&'static str, Bytes),
#[error("Data read from the network was expected to be UTF-8, but was not: {0}")]
#[diagnostic(code(cat_dev::net::parse::utf8_expected))]
Utf8Expected(#[from] FromUtf8Error),
#[error("Data read from a network slice was expected to be UTF-8, but was not: {0}")]
#[diagnostic(code(cat_dev::net::parse::utf8_expected_slice))]
Utf8ExpectedSlice(#[from] Utf8Error),
}
impl From<NetworkParseError> for CatBridgeError {
fn from(value: NetworkParseError) -> Self {
Self::Network(value.into())
}
}