1use std::{error, fmt, sync::Arc};
2
3use serde::Serialize;
4use specta::Type;
5
6use crate::internal::jsonrpc::JsonRPCError;
7
8#[derive(thiserror::Error, Debug)]
9pub enum ExecError {
10 #[error("the requested operation '{0}' is not supported by this server")]
11 OperationNotFound(String),
12 #[error("error deserializing procedure arguments: {0}")]
13 DeserializingArgErr(serde_json::Error),
14 #[error("error serializing procedure result: {0}")]
15 SerializingResultErr(serde_json::Error),
16 #[error("error in axum extractor")]
17 AxumExtractorError,
18 #[error("invalid JSON-RPC version")]
19 InvalidJsonRpcVersion,
20 #[error("method '{0}' is not supported by this endpoint.")] UnsupportedMethod(String),
22 #[error("resolver threw error")]
23 ErrResolverError(#[from] Error),
24 #[error("error creating subscription with null id")]
25 ErrSubscriptionWithNullId,
26 #[error("error creating subscription with duplicate id")]
27 ErrSubscriptionDuplicateId,
28}
29
30impl From<ExecError> for Error {
31 fn from(v: ExecError) -> Error {
32 match v {
33 ExecError::OperationNotFound(_) => Error {
34 code: ErrorCode::NotFound,
35 message: "the requested operation is not supported by this server".to_string(),
36 cause: None,
37 },
38 ExecError::DeserializingArgErr(err) => Error {
39 code: ErrorCode::BadRequest,
40 message: "error deserializing procedure arguments".to_string(),
41 cause: Some(Arc::new(err)),
42 },
43 ExecError::SerializingResultErr(err) => Error {
44 code: ErrorCode::InternalServerError,
45 message: "error serializing procedure result".to_string(),
46 cause: Some(Arc::new(err)),
47 },
48 ExecError::AxumExtractorError => Error {
49 code: ErrorCode::BadRequest,
50 message: "Error running Axum extractors on the HTTP request".into(),
51 cause: None,
52 },
53 ExecError::InvalidJsonRpcVersion => Error {
54 code: ErrorCode::BadRequest,
55 message: "invalid JSON-RPC version".into(),
56 cause: None,
57 },
58 ExecError::ErrResolverError(err) => err,
59 ExecError::UnsupportedMethod(_) => Error {
60 code: ErrorCode::BadRequest,
61 message: "unsupported metho".into(),
62 cause: None,
63 },
64 ExecError::ErrSubscriptionWithNullId => Error {
65 code: ErrorCode::BadRequest,
66 message: "error creating subscription with null request id".into(),
67 cause: None,
68 },
69 ExecError::ErrSubscriptionDuplicateId => Error {
70 code: ErrorCode::BadRequest,
71 message: "error creating subscription with duplicate id".into(),
72 cause: None,
73 },
74 }
75 }
76}
77
78impl From<ExecError> for JsonRPCError {
79 fn from(err: ExecError) -> Self {
80 let x: Error = err.into();
81 x.into()
82 }
83}
84
85#[derive(thiserror::Error, Debug)]
86pub enum ExportError {
87 #[error("IO error exporting bindings: {0}")]
88 IOErr(#[from] std::io::Error),
89}
90
91#[derive(Debug, Clone, Serialize, Type)]
92#[allow(dead_code)]
93pub struct Error {
94 pub(crate) code: ErrorCode,
95 pub(crate) message: String,
96 #[serde(skip)]
97 pub(crate) cause: Option<Arc<dyn std::error::Error + Send + Sync>>, }
99
100impl From<Error> for JsonRPCError {
101 fn from(err: Error) -> Self {
102 JsonRPCError {
103 code: err.code.to_status_code() as i32,
104 message: err.message,
105 data: None,
106 }
107 }
108}
109
110impl fmt::Display for Error {
111 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
112 write!(
113 f,
114 "rspc::Error {{ code: {:?}, message: {} }}",
115 self.code, self.message
116 )
117 }
118}
119
120impl error::Error for Error {
121 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
122 None
123 }
124}
125
126impl Error {
127 pub const fn new(code: ErrorCode, message: String) -> Self {
128 Error {
129 code,
130 message,
131 cause: None,
132 }
133 }
134
135 pub fn with_cause<TErr>(code: ErrorCode, message: String, cause: TErr) -> Self
136 where
137 TErr: std::error::Error + Send + Sync + 'static,
138 {
139 Self {
140 code,
141 message,
142 cause: Some(Arc::new(cause)),
143 }
144 }
145
146 #[doc(hidden)]
147 pub fn message(&self) -> &str {
148 &self.message
149 }
150
151 #[doc(hidden)]
152 pub fn cause(self) -> Option<Arc<dyn error::Error + Send + Sync>> {
153 self.cause
154 }
155}
156
157#[derive(Debug, Clone, Serialize, Type, PartialEq, Eq)]
159pub enum ErrorCode {
160 BadRequest,
161 Unauthorized,
162 Forbidden,
163 NotFound,
164 Timeout,
165 Conflict,
166 PreconditionFailed,
167 PayloadTooLarge,
168 MethodNotSupported,
169 ClientClosedRequest,
170 InternalServerError,
171}
172
173impl ErrorCode {
174 pub fn to_status_code(&self) -> u16 {
175 match self {
176 ErrorCode::BadRequest => 400,
177 ErrorCode::Unauthorized => 401,
178 ErrorCode::Forbidden => 403,
179 ErrorCode::NotFound => 404,
180 ErrorCode::Timeout => 408,
181 ErrorCode::Conflict => 409,
182 ErrorCode::PreconditionFailed => 412,
183 ErrorCode::PayloadTooLarge => 413,
184 ErrorCode::MethodNotSupported => 405,
185 ErrorCode::ClientClosedRequest => 499,
186 ErrorCode::InternalServerError => 500,
187 }
188 }
189
190 pub const fn from_status_code(status_code: u16) -> Option<Self> {
191 match status_code {
192 400 => Some(ErrorCode::BadRequest),
193 401 => Some(ErrorCode::Unauthorized),
194 403 => Some(ErrorCode::Forbidden),
195 404 => Some(ErrorCode::NotFound),
196 408 => Some(ErrorCode::Timeout),
197 409 => Some(ErrorCode::Conflict),
198 412 => Some(ErrorCode::PreconditionFailed),
199 413 => Some(ErrorCode::PayloadTooLarge),
200 405 => Some(ErrorCode::MethodNotSupported),
201 499 => Some(ErrorCode::ClientClosedRequest),
202 500 => Some(ErrorCode::InternalServerError),
203 _ => None,
204 }
205 }
206}