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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
//! A bunch of wrap errors.
use crate::prelude::jsonrpc_core;
use crate::prelude::rings_core;

/// A wrap `Result` contains custom errors.
pub type Result<T> = std::result::Result<T, Error>;

/// Errors enum mapping global custom errors.
/// The error type can be expressed in decimal, where the high decs represent
/// the error category and the low decs represent the error type.
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
#[repr(u32)]
pub enum Error {
    #[error("Connect remote rpc server failed: {0}.")]
    RemoteRpcError(String) = 100,
    #[error("Unknown rpc error.")]
    UnknownRpcError = 101,
    #[error("Connection not found.")]
    ConnectionNotFound = 203,
    #[error("Create connection error: {0}.")]
    NewConnectionError(rings_core::error::Error) = 204,
    #[error("Close connection error: {0}.")]
    CloseConnectionError(rings_core::error::Error) = 205,
    #[error("Invalid connection id.")]
    InvalidConnectionId = 206,
    #[error("Create offer info failed: {0}.")]
    CreateOffer(rings_core::error::Error) = 207,
    #[error("Answer offer info failed: {0}.")]
    AnswerOffer(rings_core::error::Error) = 208,
    #[error("Accept answer info failed: {0}.")]
    AcceptAnswer(rings_core::error::Error) = 209,
    #[error("Decode error.")]
    DecodeError = 300,
    #[error("Encode error.")]
    EncodeError = 301,
    #[error("WASM compile error: {0}")]
    WasmCompileError(String) = 400,
    #[error("BackendMessage RwLock Error")]
    WasmBackendMessageRwLockError = 401,
    #[error("WASM instantiation error.")]
    WasmInstantiationError = 402,
    #[error("WASM export error.")]
    WasmExportError = 403,
    #[error("WASM runtime error: {0}")]
    WasmRuntimeError(String) = 404,
    #[error("WASM global memory mutex error.")]
    WasmGlobalMemoryLockError = 405,
    #[error("WASM failed to load file.")]
    WasmFailedToLoadFile = 406,
    #[error("Invalid did.")]
    InvalidDid = 500,
    #[error("Invalid method.")]
    InvalidMethod = 501,
    #[error("Internal error.")]
    InternalError = 502,
    #[error("No Permission")]
    NoPermission = 504,
    #[error("Connect error, {0}")]
    ConnectError(rings_core::error::Error) = 600,
    #[error("Send message error: {0}")]
    SendMessage(rings_core::error::Error) = 601,
    #[error("vnode action error: {0}")]
    VNodeError(rings_core::error::Error) = 603,
    #[error("service register action error: {0}")]
    ServiceRegisterError(rings_core::error::Error) = 604,
    #[error("JsError: {0}")]
    JsError(String) = 700,
    #[error("Invalid message")]
    InvalidMessage = 800,
    #[error("Invalid http request: {0}")]
    HttpRequestError(String) = 801,
    #[error("Invalid data")]
    InvalidData = 802,
    #[error("Invalid service")]
    InvalidService = 803,
    #[error("Invalid address")]
    InvalidAddress = 804,
    #[error("Invalid auth data")]
    InvalidAuthData = 805,
    #[error("invalid headers")]
    InvalidHeaders = 806,
    #[error("Storage Error: {0}")]
    Storage(rings_core::error::Error) = 807,
    #[error("Swarm Error: {0}")]
    Swarm(rings_core::error::Error) = 808,
    #[error("Create File Error: {0}")]
    CreateFileError(String) = 900,
    #[error("Open File Error: {0}")]
    OpenFileError(String) = 901,
    #[error("acquire lock failed")]
    Lock = 902,
    #[error("serde json error: {0}")]
    SerdeJsonError(#[from] serde_json::Error) = 1000,
    #[error("serde yaml error: {0}")]
    SerdeYamlError(#[from] serde_yaml::Error) = 1001,
    #[error("verify error: {0}")]
    VerifyError(String) = 1002,
    #[error("core error: {0}")]
    CoreError(#[from] rings_core::error::Error) = 1102,
    #[error("external singer error: {0}")]
    ExternalError(String) = 1202,
}

impl Error {
    fn discriminant(&self) -> u32 {
        // SAFETY: Because `Self` is marked `repr(u32)`, its layout is a `repr(C)` `union`
        // between `repr(C)` structs, each of which has the `u32` discriminant as its first
        // field, so we can read the discriminant without offsetting the pointer.
        // This code is copy from
        // ref: https://doc.rust-lang.org/std/mem/fn.discriminant.html
        // And we modify it from [u8] to [u32], this is work because
        // repr(C) is equivalent to one of repr(u*) (see the next section) for
        // fieldless enums.
        // ref: https://doc.rust-lang.org/nomicon/other-reprs.html
        unsafe { *<*const _>::from(self).cast::<u32>() }
    }

    pub fn code(&self) -> u32 {
        self.discriminant()
    }
}

impl From<Error> for jsonrpc_core::Error {
    fn from(e: Error) -> Self {
        Self {
            code: jsonrpc_core::ErrorCode::ServerError(e.code().into()),
            message: e.to_string(),
            data: None,
        }
    }
}

impl From<crate::prelude::rings_rpc::error::Error> for Error {
    fn from(e: crate::prelude::rings_rpc::error::Error) -> Self {
        match e {
            rings_rpc::error::Error::DecodeError => Error::DecodeError,
            rings_rpc::error::Error::EncodeError => Error::EncodeError,
            rings_rpc::error::Error::InvalidMethod => Error::InvalidMethod,
            rings_rpc::error::Error::RpcError(v) => Error::RemoteRpcError(v.to_string()),
            rings_rpc::error::Error::InvalidSignature => Error::InvalidData,
            rings_rpc::error::Error::InvalidHeaders => Error::InvalidHeaders,
            _ => Error::UnknownRpcError,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_error_code() {
        let err = Error::RemoteRpcError("Test".to_string());
        assert_eq!(err.code(), 100);
    }
}

#[cfg(feature = "browser")]
impl From<Error> for wasm_bindgen::JsValue {
    fn from(err: Error) -> Self {
        wasm_bindgen::JsValue::from_str(&err.to_string())
    }
}