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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
//! # Panda error types
//!
//! Here there are some wrappers of [Discord API errors](https://discordapp.com/developers/docs/topics/opcodes-and-status-codes)

use std::{error::Error, fmt, result::Result as StdResult};
use tokio_tungstenite::tungstenite::Error as TungsteniteError;

/// This library use a shared result type, because all functions returns the same error type
pub type Result<T> = StdResult<T, PandaError>;

/// The error enum for Panda
#[derive(Debug)]
pub enum PandaError {
    /// Returned when there was an authentication error and the gateway is closed
    AuthenticationFailed,

    /// Returned when "discord" fails to connect to the gateway, it can only be returned at
    /// the first connection, all reconnections are handled by "discord"
    CantConnectToGateway,

    /// Returned when the gateway connection is unexpected closed
    ConnectionClosed,

    /// Returned when "discord" receives a unknown message format
    UnknownPayloadReceived,

    /// Returned when panda sent an invalid Opcode
    UnknownOpcodeSent,

    /// Returned when panda sent an invalid payload
    InvalidDecodeSent,

    /// Returned when "discord" recevies a invalid message format
    InvalidPayloadFormat,

    /// Returned when "discord" receives a unexpected message format like IDENTIFY
    UnexpectedPayloadReceived,

    /// Returned when "discord" receives a not zlib compressed payload
    WrongCompression,

    // Http Errors
    /// Returned when "discord" http client didn't receive a response of
    /// Discord API
    HttpNoResponse,

    /// Returned when http request format was invalid
    HttpImproperlyFormatted,

    /// Returned when http request has an invalid token
    HttpUnauthorized,

    /// Returned when the client doesn't have enough permissions
    HttpForbidden,

    /// Returned when the http request URL had invalid parameters,
    /// such as wrong {channel_id}
    HttpInvalidParameters,

    /// Returned when the gateway couldn't close the connection succesfully
    UnsuccessfulConnectionClose,

    /// Returned when you send an invalid shard
    InvalidShard,

    /// Returned when you handled too many guilds, and shard is necessary
    ShardingRequired,

    // Invalid API version (gateway)
    InvalidApiGatewayVersion,

    /// serde_json
    SerdeError(serde_json::Error),

    /// tungstenite
    TungsteniteError(TungsteniteError),
}

impl fmt::Display for PandaError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::AuthenticationFailed => write!(f, "Authentication failed"),
            Self::CantConnectToGateway => write!(f, "'Discord' couldn't connect to gateway"),
            Self::ConnectionClosed => write!(f, "Connection closed unexpectedly"),
            Self::UnknownPayloadReceived => write!(f, "Unknown payload format received"),
            Self::InvalidPayloadFormat => write!(f, "Invalid payload format received",),
            Self::UnexpectedPayloadReceived => write!(f, "Unexpected payload received"),
            Self::WrongCompression => write!(f, "Wrong zlib compression"),
            Self::HttpNoResponse => write!(f, "Discord HTTP API didn't response"),
            Self::HttpImproperlyFormatted => write!(f, "Invalid format of request body"),
            Self::HttpUnauthorized => write!(f, "The client token is invalid"),
            Self::HttpForbidden => write!(f, "The client did not have permission to the resource"),
            Self::HttpInvalidParameters => write!(f, "The request had invalid parameters"),
            Self::UnsuccessfulConnectionClose => write!(f, "The gateway couldn't close succesfully the connection"),
            Self::InvalidShard => write!(f, "You sent an invalid shard"),
            Self::ShardingRequired => write!(f, "The SessionData would have handled too many guilds - you are required to shard your connection in order to connect."),
            Self::InvalidApiGatewayVersion => write!(f, "panda needs to update the gateway version"),
            Self::SerdeError(e) => write!(f, "Serde Error: {}", e),
            Self::TungsteniteError(e) => write!(f, "Tungstenite Error: {}", e),
            Self::UnknownOpcodeSent => write!(f, "panda sent an invalid Opcode, please report the bug"),
            Self::InvalidDecodeSent => write!(f, "panda sent an invalid payload, please report the bug"),
        }
    }
}

impl Error for PandaError {}

// Error parsing
impl From<serde_json::Error> for PandaError {
    fn from(error: serde_json::Error) -> Self {
        // InvalidPayloadFormat

        if error.is_data() {
            return PandaError::InvalidPayloadFormat;
        }

        PandaError::SerdeError(error)
    }
}

impl From<TungsteniteError> for PandaError {
    fn from(error: TungsteniteError) -> Self {
        // TODO: Improve this (IO) errors

        match error {
            TungsteniteError::ConnectionClosed => PandaError::ConnectionClosed,
            _ => PandaError::TungsteniteError(error),
        }
    }
}

impl From<isahc::Error> for PandaError {
    fn from(_error: isahc::Error) -> Self { 
        
        // TODO: add match
        PandaError::HttpNoResponse
    }
}