1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
use std::{fmt, io, net::SocketAddr};

use crate::HostAddress;

#[derive(Debug)]
pub enum Error {
    JumpHostConnectFail {
        jump_host_addr: HostAddress<'static>,
        io_error: io::Error,
    },
    AsyncSessionInitialize(io::Error),
    DnsResolverCreate(async_std_resolver::ResolveError),
    DnsResolverLookup(async_std_resolver::ResolveError),
    JumpHostIpResolutionFail {
        jump_host_addr: String,
    },
    LocalSocketAddr {
        local_socket: SocketAddr,
        io_error: io::Error,
    },
    LocalSocketBind {
        local_socket: SocketAddr,
        io_error: io::Error,
    },
    PrivateKeyPlainPath(plain_path::HomeDirNotFound),
    SshHandshakeFail(io::Error),
    SshUserAuthFail(io::Error),
    SshUserAuthError(async_ssh2_lite::ssh2::Error),
    SshUserAuthUnknownError,
    SshTunnelOpenFail(io::Error),
    SshTunnelListenerCreate(io::Error),
    SshTunnelStreamCreate(io::Error),
    SshStreamerSpawnFail(tokio::task::JoinError),
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Self::JumpHostConnectFail { jump_host_addr, .. } => {
                write!(f, "Failed to connect to jump host: `{}`.", jump_host_addr)
            }
            Self::AsyncSessionInitialize(..) => {
                write!(f, "Failed to initialize SSH session with jump host.")
            }
            Self::DnsResolverCreate(..) => write!(f, "Failed to construct DNS resolver."),
            Self::DnsResolverLookup(resolve_error) => {
                write!(f, "Failed to lookup IP for jump host: `{}`.", resolve_error)
            }
            Self::JumpHostIpResolutionFail { jump_host_addr } => {
                write!(
                    f,
                    "Failed to find IPv4 address for jump host: `{}`.",
                    jump_host_addr
                )
            }
            Self::LocalSocketAddr { local_socket, .. } => write!(
                f,
                "Failed to retrieve socket address after binding to: {}.",
                local_socket
            ),
            Self::LocalSocketBind { local_socket, .. } => {
                write!(f, "Failed to bind to local socket: {}.", local_socket)
            }
            Self::PrivateKeyPlainPath(..) => write!(f, "Failed to get private key plain path."),
            Self::SshHandshakeFail(..) => write!(f, "SSH handshake with jump host failed."),
            Self::SshUserAuthFail(..) => write!(f, "SSH user auth with jump host failed."),
            Self::SshUserAuthError(..) => write!(f, "SSH user auth with jump host failed.",),
            Self::SshUserAuthUnknownError => {
                write!(f, "SSH user auth with jump host failed with unknown cause.")
            }
            Self::SshTunnelOpenFail(..) => write!(f, "Failed to open SSH tunnel."),
            Self::SshTunnelListenerCreate(..) => write!(f, "Failed to spawn SSH tunnel listener"),
            Self::SshTunnelStreamCreate(..) => write!(f, "Failed to spawn SSH tunnel stream."),
            Self::SshStreamerSpawnFail(..) => write!(f, "Failed to join SSH tunnel streamer task."),
        }
    }
}

impl std::error::Error for Error {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        match self {
            Self::JumpHostConnectFail { io_error, .. } => Some(io_error),
            Self::AsyncSessionInitialize(io_error) => Some(io_error),
            Self::DnsResolverCreate(resolve_error) => Some(resolve_error),
            Self::DnsResolverLookup(resolve_error) => Some(resolve_error),
            Self::JumpHostIpResolutionFail { .. } => None,
            Self::LocalSocketAddr { io_error, .. } => Some(io_error),
            Self::LocalSocketBind { io_error, .. } => Some(io_error),
            Self::PrivateKeyPlainPath(home_dir_not_found) => Some(home_dir_not_found),
            Self::SshHandshakeFail(io_error) => Some(io_error),
            Self::SshUserAuthFail(io_error) => Some(io_error),
            Self::SshUserAuthError(ssh2_error) => Some(ssh2_error),
            Self::SshUserAuthUnknownError => None,
            Self::SshTunnelOpenFail(io_error) => Some(io_error),
            Self::SshTunnelListenerCreate(io_error) => Some(io_error),
            Self::SshTunnelStreamCreate(io_error) => Some(io_error),
            Self::SshStreamerSpawnFail(join_error) => Some(join_error),
        }
    }
}