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::<()>),
}
}