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
144impl ResponseForPanic for PanicHandler {
145 type ResponseBody = axum::body::Body;
146
147 fn response_for_panic(
148 &mut self,
149 err: Box<dyn Any + Send + 'static>,
150 ) -> axum::http::Response<Self::ResponseBody> {
151 self.metrics.requests_panicked.inc();
152
153 let err = if let Some(s) = err.downcast_ref::<String>() {
154 anyhow::anyhow!(s.clone()).context("Request panicked")
155 } else if let Some(s) = err.downcast_ref::<&str>() {
156 anyhow::anyhow!(s.to_string()).context("Request panicked")
157 } else {
158 anyhow::anyhow!("Request panicked")
159 };
160
161 let err: RpcError = err.into();
162 let err: ErrorObject<'static> = err.into();
163
164 let resp = json!({
165 "jsonrpc": "2.0",
166 "error": err,
167 "id": null,
168 });
169
170 Json(resp).into_response()
171 }
172}