sui_indexer_alt_jsonrpc/
error.rs1use std::any::Any;
5use std::convert::Infallible;
6use std::fmt::Display;
7use std::sync::Arc;
8
9use axum::Json;
10use axum::response::IntoResponse;
11use jsonrpsee::types::ErrorObject;
12use jsonrpsee::types::error::INTERNAL_ERROR_CODE;
13use jsonrpsee::types::error::INVALID_PARAMS_CODE;
14use serde_json::json;
15use tower_http::catch_panic::ResponseForPanic;
16
17use crate::metrics::RpcMetrics;
18
19pub const TIMEOUT_ERROR_CODE: i32 = -32604;
21
22macro_rules! rpc_bail {
24 ($($arg:tt)*) => {
25 return Err(crate::error::internal_error!($($arg)*))
26 };
27}
28
29macro_rules! internal_error {
31 ($($arg:tt)*) => {
32 crate::error::RpcError::InternalError(anyhow::anyhow!($($arg)*))
33 };
34}
35
36pub(crate) use internal_error;
37pub(crate) use rpc_bail;
38
39pub(crate) trait InternalContext<T, E: std::error::Error> {
41 fn internal_context<C>(self, ctx: C) -> Result<T, RpcError<E>>
42 where
43 C: Display + Send + Sync + 'static;
44
45 fn with_internal_context<C, F>(self, f: F) -> Result<T, RpcError<E>>
46 where
47 C: Display + Send + Sync + 'static,
48 F: FnOnce() -> C;
49}
50
51#[derive(thiserror::Error, Debug)]
68pub(crate) enum RpcError<E: std::error::Error = Infallible> {
69 #[error("Invalid Params: {0}")]
70 InvalidParams(E),
71
72 #[error("Timed out: {0}")]
73 Timeout(anyhow::Error),
74
75 #[error("Internal Error: {0:#}")]
76 InternalError(#[from] anyhow::Error),
77}
78
79#[derive(Clone)]
82pub(crate) struct PanicHandler {
83 metrics: Arc<RpcMetrics>,
84}
85
86impl PanicHandler {
87 pub fn new(metrics: Arc<RpcMetrics>) -> Self {
88 Self { metrics }
89 }
90}
91
92impl<T, E: std::error::Error> InternalContext<T, E> for Result<T, RpcError<E>> {
93 fn internal_context<C>(self, ctx: C) -> Result<T, RpcError<E>>
95 where
96 C: Display + Send + Sync + 'static,
97 {
98 use RpcError as E;
99 match self {
100 Err(E::InternalError(e)) => Err(E::InternalError(e.context(ctx))),
101 Err(E::Timeout(e)) => Err(E::Timeout(e.context(ctx))),
102 _ => self,
103 }
104 }
105
106 fn with_internal_context<C, F>(self, f: F) -> Result<T, RpcError<E>>
109 where
110 C: Display + Send + Sync + 'static,
111 F: FnOnce() -> C,
112 {
113 use RpcError as E;
114 match self {
115 Err(E::InternalError(e)) => Err(E::InternalError(e.context(f()))),
116 Err(E::Timeout(e)) => Err(E::Timeout(e.context(f()))),
117 _ => self,
118 }
119 }
120}
121
122impl<E: std::error::Error> From<RpcError<E>> for ErrorObject<'static> {
123 fn from(err: RpcError<E>) -> Self {
124 use RpcError as E;
125 match &err {
126 E::InvalidParams(_) => {
127 ErrorObject::owned(INVALID_PARAMS_CODE, err.to_string(), None::<()>)
128 }
129
130 E::Timeout(_) => ErrorObject::owned(TIMEOUT_ERROR_CODE, err.to_string(), None::<()>),
131
132 E::InternalError(_) => {
133 ErrorObject::owned(INTERNAL_ERROR_CODE, err.to_string(), None::<()>)
134 }
135 }
136 }
137}
138
139pub(crate) fn invalid_params<E: std::error::Error>(err: E) -> RpcError<E> {
141 RpcError::InvalidParams(err)
142}
143
144pub(crate) fn client_error_to_error_object(
146 error: jsonrpsee::core::ClientError,
147) -> ErrorObject<'static> {
148 match error {
149 jsonrpsee::core::ClientError::Call(e) => e,
153 _ => ErrorObject::owned(INTERNAL_ERROR_CODE, error.to_string(), None::<()>),
154 }
155}
156
157impl ResponseForPanic for PanicHandler {
158 type ResponseBody = axum::body::Body;
159
160 fn response_for_panic(
161 &mut self,
162 err: Box<dyn Any + Send + 'static>,
163 ) -> axum::http::Response<Self::ResponseBody> {
164 self.metrics.requests_panicked.inc();
165
166 let err = if let Some(s) = err.downcast_ref::<String>() {
167 anyhow::anyhow!(s.clone()).context("Request panicked")
168 } else if let Some(s) = err.downcast_ref::<&str>() {
169 anyhow::anyhow!(s.to_string()).context("Request panicked")
170 } else {
171 anyhow::anyhow!("Request panicked")
172 };
173
174 let err: RpcError = err.into();
175 let err: ErrorObject<'static> = err.into();
176
177 let resp = json!({
178 "jsonrpc": "2.0",
179 "error": err,
180 "id": null,
181 });
182
183 Json(resp).into_response()
184 }
185}