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
139
140
141
142
use crate::{ErrorPayload, RpcReturn};
use serde_json::value::RawValue;

/// An RPC error.
#[derive(Debug, thiserror::Error)]
pub enum RpcError<E, ErrResp = Box<RawValue>> {
    /// Server returned an error response.
    #[error("server returned an error response: {0}")]
    ErrorResp(ErrorPayload<ErrResp>),

    /// Server returned a null response when a non-null response was expected.
    #[error("server returned a null response when a non-null response was expected")]
    NullResp,

    /// Rpc server returned an unsupported feature.
    #[error("unsupported feature: {0}")]
    UnsupportedFeature(&'static str),

    /// Returned when a local pre-processing step fails. This allows custom
    /// errors from local signers or request pre-processors.
    #[error("local usage error: {0}")]
    LocalUsageError(#[source] Box<dyn std::error::Error + Send + Sync>),

    /// JSON serialization error.
    #[error("serialization error: {0}")]
    SerError(
        /// The underlying serde_json error.
        // To avoid accidentally confusing ser and deser errors, we do not use
        // the `#[from]` tag.
        #[source]
        serde_json::Error,
    ),
    /// JSON deserialization error.
    #[error("deserialization error: {err}")]
    DeserError {
        /// The underlying serde_json error.
        // To avoid accidentally confusing ser and deser errors, we do not use
        // the `#[from]` tag.
        #[source]
        err: serde_json::Error,
        /// For deser errors, the text that failed to deserialize.
        text: String,
    },

    /// Transport error.
    ///
    /// This variant is used when the error occurs during communication.
    #[error(transparent)]
    Transport(
        /// The underlying transport error.
        #[from]
        E,
    ),
}

impl<E, ErrResp> RpcError<E, ErrResp>
where
    ErrResp: RpcReturn,
{
    /// Instantiate a new `ErrorResp` from an error response.
    pub const fn err_resp(err: ErrorPayload<ErrResp>) -> Self {
        Self::ErrorResp(err)
    }

    /// Instantiate a new `LocalUsageError` from a custom error.
    pub fn local_usage(err: impl std::error::Error + Send + Sync + 'static) -> Self {
        Self::LocalUsageError(err.into())
    }

    /// Instantiate a new `LocalUsageError` from a custom error message.
    pub fn local_usage_str(err: &str) -> Self {
        Self::LocalUsageError(err.into())
    }

    /// Instantiate a new `DeserError` from a [`serde_json::Error`] and the
    /// text. This should be called when the error occurs during
    /// deserialization.
    ///
    /// Note: This will check if the response is actually an [ErrorPayload], if so it will return a
    /// [RpcError::ErrorResp].
    pub fn deser_err(err: serde_json::Error, text: impl AsRef<str>) -> Self {
        let text = text.as_ref();

        // check if the response is actually an `ErrorPayload`
        if let Ok(err) = serde_json::from_str::<ErrorPayload<ErrResp>>(text) {
            return Self::ErrorResp(err);
        }

        Self::DeserError { err, text: text.to_owned() }
    }
}

impl<E, ErrResp> RpcError<E, ErrResp> {
    /// Instantiate a new `SerError` from a [`serde_json::Error`]. This
    /// should be called when the error occurs during serialization.
    pub const fn ser_err(err: serde_json::Error) -> Self {
        Self::SerError(err)
    }

    /// Check if the error is a serialization error.
    pub const fn is_ser_error(&self) -> bool {
        matches!(self, Self::SerError(_))
    }

    /// Check if the error is a deserialization error.
    pub const fn is_deser_error(&self) -> bool {
        matches!(self, Self::DeserError { .. })
    }

    /// Check if the error is a transport error.
    pub const fn is_transport_error(&self) -> bool {
        matches!(self, Self::Transport(_))
    }

    /// Check if the error is an error response.
    pub const fn is_error_resp(&self) -> bool {
        matches!(self, Self::ErrorResp(_))
    }

    /// Check if the error is a null response.
    pub const fn is_null_resp(&self) -> bool {
        matches!(self, Self::NullResp)
    }

    /// Check if the error is an unsupported feature error.
    pub const fn is_unsupported_feature(&self) -> bool {
        matches!(self, Self::UnsupportedFeature(_))
    }

    /// Check if the error is a local usage error.
    pub const fn is_local_usage_error(&self) -> bool {
        matches!(self, Self::LocalUsageError(_))
    }

    /// Fallible conversion to an error response.
    pub const fn as_error_resp(&self) -> Option<&ErrorPayload<ErrResp>> {
        match self {
            Self::ErrorResp(err) => Some(err),
            _ => None,
        }
    }
}