use std::fmt::{self, Display};
use std::sync::Arc;
use futures::task::SpawnError;
use thiserror::Error;
use tor_circmgr::TargetPorts;
use tor_error::{ErrorKind, HasKind};
use crate::TorAddrError;
#[derive(Error, Clone, Debug)]
pub struct Error {
#[source]
detail: Box<ErrorDetail>,
}
impl From<ErrorDetail> for Error {
fn from(detail: ErrorDetail) -> Error {
Error {
detail: detail.into(),
}
}
}
#[cfg(feature = "error_detail")]
macro_rules! pub_if_error_detail {
{ $(#[$meta:meta])* enum $e:ident $tt:tt } => {
$(#[$meta])* pub enum $e $tt
}
}
#[cfg(not(feature = "error_detail"))]
macro_rules! pub_if_error_detail {
{ $(#[$meta:meta])* enum $e:ident $tt:tt } => {
$(#[$meta])* pub(crate) enum $e $tt }
}
pub_if_error_detail! {
#[derive(Error, Clone, Debug)]
#[non_exhaustive]
enum ErrorDetail {
#[error("Error setting up the circuit manager {0}")]
CircMgrSetup(#[source] tor_circmgr::Error),
#[error("Failed to obtain exit circuit for {exit_ports}")]
ObtainExitCircuit {
exit_ports: TargetPorts,
#[source]
cause: tor_circmgr::Error,
},
#[error("Directory state error {0}")]
DirMgr(#[from] tor_dirmgr::Error),
#[error("Protocol error while launching a stream: {0}")]
Proto(#[from] tor_proto::Error),
#[error("Error from state manager: {0}")]
Persist(#[from] tor_persist::Error),
#[error("exit timed out")]
ExitTimeout,
#[error("Rejecting .onion address as unsupported.")]
OnionAddressNotSupported,
#[error("Could not parse target address: {0}")]
Address(#[from] crate::address::TorAddrError),
#[error("Rejecting hostname as invalid.")]
InvalidHostname,
#[error("Cannot connect to a local-only address without enabling allow_local_addrs")]
LocalAddress,
#[error("Configuration failed: {0}")]
Configuration(#[from] tor_config::ConfigBuildError),
#[error("Reconfiguration failed: {0}")]
Reconfigure(#[from] tor_config::ReconfigureError),
#[error("unable to spawn {spawning}")]
Spawn {
spawning: &'static str,
#[source]
cause: Arc<SpawnError>
},
#[error("cannot {action} with unbootstrapped client")]
BootstrapRequired {
action: &'static str
},
}
}
#[cfg(feature = "error_detail")]
impl Error {
pub fn detail(&self) -> &ErrorDetail {
&self.detail
}
}
impl ErrorDetail {
pub(crate) fn from_spawn(spawning: &'static str, err: SpawnError) -> ErrorDetail {
ErrorDetail::Spawn {
spawning,
cause: Arc::new(err),
}
}
}
impl Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "tor: {}: {}", self.detail.kind(), &self.detail)
}
}
impl tor_error::HasKind for Error {
fn kind(&self) -> ErrorKind {
self.detail.kind()
}
}
impl tor_error::HasKind for ErrorDetail {
fn kind(&self) -> ErrorKind {
use ErrorDetail as E;
use ErrorKind as EK;
match self {
E::ObtainExitCircuit { cause, .. } => cause.kind(),
E::ExitTimeout => EK::RemoteNetworkTimeout,
E::BootstrapRequired { .. } => EK::BootstrapRequired,
E::CircMgrSetup(e) => e.kind(),
E::DirMgr(e) => e.kind(),
E::Proto(e) => e.kind(),
E::Persist(e) => e.kind(),
E::Configuration(e) => e.kind(),
E::Reconfigure(e) => e.kind(),
E::Spawn { cause, .. } => cause.kind(),
E::OnionAddressNotSupported => EK::NotImplemented,
E::Address(_) | E::InvalidHostname => EK::InvalidStreamTarget,
E::LocalAddress => EK::ForbiddenStreamTarget,
}
}
}
impl From<TorAddrError> for Error {
fn from(e: TorAddrError) -> Error {
e.into()
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn traits_ok() {
fn assert<
T: Send + Sync + Clone + std::fmt::Debug + Display + std::error::Error + 'static,
>() {
}
fn check() {
assert::<Error>();
assert::<ErrorDetail>();
}
check(); }
}