sui_indexer_alt_jsonrpc/
error.rs

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
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0
//

use std::{convert::Infallible, fmt::Display};

use jsonrpsee::types::{
    error::{INTERNAL_ERROR_CODE, INVALID_PARAMS_CODE},
    ErrorObject,
};

/// Like anyhow's `bail!`, but for returning an internal error.
macro_rules! rpc_bail {
    ($($arg:tt)*) => {
        return Err(crate::error::internal_error!($($arg)*))
    };
}

/// Like anyhow's `anyhow!`, but for returning an internal error.
macro_rules! internal_error {
    ($($arg:tt)*) => {
        crate::error::RpcError::InternalError(anyhow::anyhow!($($arg)*))
    };
}

pub(crate) use internal_error;
pub(crate) use rpc_bail;

/// Behaves exactly like `anyhow::Context`, but only adds context to `RpcError::InternalError`.
pub(crate) trait InternalContext<T, E: std::error::Error> {
    fn internal_context<C>(self, ctx: C) -> Result<T, RpcError<E>>
    where
        C: Display + Send + Sync + 'static;

    fn with_internal_context<C, F>(self, f: F) -> Result<T, RpcError<E>>
    where
        C: Display + Send + Sync + 'static,
        F: FnOnce() -> C;
}

/// This type represents two kinds of errors: Invalid Params (the user's fault), and Internal
/// Errors (the service's fault). Each RpcModule is responsible for defining its own structured
/// user errors, while internal errors are represented with anyhow everywhere.
///
/// The internal error type defaults to `Infallible`, meaning there are no reasons the response
/// might fail because of user input.
///
/// This representation was chosen to encourage a pattern where errors that are presented to users
/// have a single source of truth for how they should be displayed, while internal errors encourage
/// the addition of context (extra information to build a trace of why something went wrong).
///
/// User errors must be explicitly wrapped with `invalid_params` while internal errors are
/// implicitly converted using the `?` operator. This asymmetry comes from the fact that we could
/// populate `E` with `anyhow::Error`, which would then cause `From` impls to overlap if we
/// supported conversion from both `E` and `anyhow::Error`.
#[derive(thiserror::Error, Debug)]
pub(crate) enum RpcError<E: std::error::Error = Infallible> {
    #[error("Invalid Params: {0}")]
    InvalidParams(E),

    #[error("Internal Error: {0:#}")]
    InternalError(#[from] anyhow::Error),
}

impl<T, E: std::error::Error> InternalContext<T, E> for Result<T, RpcError<E>> {
    /// Wrap an internal error with additional context.
    fn internal_context<C>(self, ctx: C) -> Result<T, RpcError<E>>
    where
        C: Display + Send + Sync + 'static,
    {
        use RpcError as E;
        if let Err(E::InternalError(e)) = self {
            Err(E::InternalError(e.context(ctx)))
        } else {
            self
        }
    }

    /// Wrap an internal error with additional context that is lazily evaluated only once an
    /// internal error has occured.
    fn with_internal_context<C, F>(self, f: F) -> Result<T, RpcError<E>>
    where
        C: Display + Send + Sync + 'static,
        F: FnOnce() -> C,
    {
        use RpcError as E;
        if let Err(E::InternalError(e)) = self {
            Err(E::InternalError(e.context(f())))
        } else {
            self
        }
    }
}

impl<E: std::error::Error> From<RpcError<E>> for ErrorObject<'static> {
    fn from(err: RpcError<E>) -> Self {
        use RpcError as E;
        match &err {
            E::InvalidParams(_) => {
                ErrorObject::owned(INVALID_PARAMS_CODE, err.to_string(), None::<()>)
            }

            E::InternalError(_) => {
                ErrorObject::owned(INTERNAL_ERROR_CODE, err.to_string(), None::<()>)
            }
        }
    }
}

/// Helper function to convert a user error into the `RpcError` type.
pub(crate) fn invalid_params<E: std::error::Error>(err: E) -> RpcError<E> {
    RpcError::InvalidParams(err)
}

/// Helper function to convert a jsonrpc client error into an `ErrorObject`.
pub(crate) fn client_error_to_error_object(
    error: jsonrpsee::core::ClientError,
) -> ErrorObject<'static> {
    match error {
        // `Call` is the only error type that actually conveys meaningful error
        // from a user calling the method. Other error variants are all more or less
        // internal errors.
        jsonrpsee::core::ClientError::Call(e) => e,
        _ => ErrorObject::owned(INTERNAL_ERROR_CODE, error.to_string(), None::<()>),
    }
}