Skip to content

Commit e1b40dc

Browse files
committed
better error handling
1 parent d39e1e6 commit e1b40dc

3 files changed

Lines changed: 102 additions & 6 deletions

File tree

examples/ws-echo/main.rs

Lines changed: 66 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use endpoint_libs::libs::handler::RequestHandler;
77
use endpoint_libs::libs::log::error_aggregation::ErrorAggregationConfig;
88
use endpoint_libs::libs::log::{LogLevel, LoggingConfig, OtelConfig, setup_logging};
99
use endpoint_libs::libs::toolbox::{ArcToolbox, RequestContext};
10-
use endpoint_libs::libs::ws::toolbox::CustomError;
10+
use endpoint_libs::libs::ws::toolbox::{CustomError, HandlerError};
1111
use endpoint_libs::libs::ws::{
1212
AuthController, WebsocketServer, WsConnection, WsRequest, WsResponse, WsServerConfig,
1313
};
@@ -88,20 +88,81 @@ impl WsResponse for HoneyReceiveUserInfoResponse {
8888
type Request = HoneyReceiveUserInfoRequest;
8989
}
9090

91+
// --- Typed error enum example ---
92+
//
93+
// This demonstrates the HandlerError trait for defining per-handler error codes.
94+
95+
#[derive(Debug, Clone)]
96+
pub enum EchoTypedError {
97+
MessageTooLong { max_length: u32, actual_length: u32 },
98+
EmptyMessage,
99+
}
100+
101+
impl std::fmt::Display for EchoTypedError {
102+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
103+
match self {
104+
Self::MessageTooLong { max_length, actual_length } => {
105+
write!(f, "message length {} exceeds maximum {}", actual_length, max_length)
106+
}
107+
Self::EmptyMessage => write!(f, "message cannot be empty"),
108+
}
109+
}
110+
}
111+
112+
impl std::error::Error for EchoTypedError {}
113+
114+
impl HandlerError for EchoTypedError {
115+
fn error_code(&self) -> ErrorCode {
116+
match self {
117+
// Handler-specific codes for Echo (method 1): 1001-1099
118+
Self::MessageTooLong { .. } => ErrorCode::new(1001),
119+
Self::EmptyMessage => ErrorCode::new(1002),
120+
}
121+
}
122+
123+
fn params(&self) -> String {
124+
match self {
125+
Self::MessageTooLong { max_length, actual_length } => {
126+
format!("message length {} exceeds maximum {}", actual_length, max_length)
127+
}
128+
Self::EmptyMessage => "empty message not allowed".to_string(),
129+
}
130+
}
131+
}
132+
133+
impl From<EchoTypedError> for CustomError {
134+
fn from(err: EchoTypedError) -> Self {
135+
CustomError::new(err.error_code(), err.params())
136+
}
137+
}
138+
91139
// --- Handlers ---
92140

93141
pub struct MethodEcho;
94142

95143
#[async_trait(?Send)]
96144
impl RequestHandler for MethodEcho {
97145
type Request = EchoRequest;
146+
type Error = EchoTypedError;
98147

99-
async fn handle(&self, ctx: RequestContext, req: EchoRequest) -> Result<EchoResponse> {
148+
async fn handle(&self, ctx: RequestContext, req: EchoRequest) -> Result<EchoResponse, EchoTypedError> {
100149
tracing::info!(
101150
conn_id = %ctx.connection_id,
102151
message = %req.message,
103152
"Echo request received"
104153
);
154+
155+
if req.message.is_empty() {
156+
return Err(EchoTypedError::EmptyMessage);
157+
}
158+
159+
if req.message.len() > 1000 {
160+
return Err(EchoTypedError::MessageTooLong {
161+
max_length: 1000,
162+
actual_length: req.message.len() as u32,
163+
});
164+
}
165+
105166
let response = EchoResponse {
106167
message: format!("echo: {}", req.message),
107168
};
@@ -119,12 +180,13 @@ pub struct MethodReceiveUserInfo;
119180
#[async_trait(?Send)]
120181
impl RequestHandler for MethodReceiveUserInfo {
121182
type Request = HoneyReceiveUserInfoRequest;
183+
type Error = CustomError;
122184

123185
async fn handle(
124186
&self,
125187
ctx: RequestContext,
126188
req: HoneyReceiveUserInfoRequest,
127-
) -> Result<HoneyReceiveUserInfoResponse> {
189+
) -> Result<HoneyReceiveUserInfoResponse, CustomError> {
128190
tracing::info!(
129191
conn_id = %ctx.connection_id,
130192
user_pub_id = %req.user_pub_id,
@@ -147,7 +209,7 @@ impl RequestHandler for MethodReceiveUserInfo {
147209
user_pub_id = %req.user_pub_id,
148210
"Rejecting ReceiveUserInfo with BAD_REQUEST"
149211
);
150-
Err(CustomError::new(ErrorCode::BAD_REQUEST, msg).into())
212+
Err(CustomError::new(ErrorCode::BAD_REQUEST, msg))
151213
}
152214
}
153215

src/libs/ws/handler.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,17 @@ use crate::libs::{
77
toolbox::{ArcToolbox, RequestContext, Toolbox},
88
ws::{WsRequest, request_error_to_resp},
99
};
10+
use crate::libs::ws::toolbox::{CustomError, HandlerError};
1011

1112
#[allow(type_alias_bounds)]
12-
pub type Response<T: WsRequest> = Result<T::Response>;
13+
pub type Response<T: WsRequest, E: HandlerError = CustomError> = Result<T::Response, E>;
14+
1315
#[async_trait(?Send)]
1416
pub trait RequestHandler: Send + Sync {
1517
type Request: WsRequest + 'static;
18+
type Error: HandlerError + Into<eyre::Error>;
1619

17-
async fn handle(&self, ctx: RequestContext, req: Self::Request) -> Response<Self::Request>;
20+
async fn handle(&self, ctx: RequestContext, req: Self::Request) -> Response<Self::Request, Self::Error>;
1821
}
1922

2023
#[doc(hidden)]
@@ -53,6 +56,15 @@ impl<T: RequestHandler> RequestHandlerErased for T {
5356
let fut = RequestHandler::handle(self, ctx.clone(), data);
5457

5558
let resp = fut.await;
59+
// Convert handler error to eyre::Result via CustomError
60+
let resp: eyre::Result<<T::Request as WsRequest>::Response> = match resp {
61+
Ok(ok) => Ok(ok),
62+
Err(err) => {
63+
let custom: CustomError = err.into();
64+
Err(custom.into())
65+
}
66+
};
67+
5668
if let Some(resp) = Toolbox::encode_ws_response(ctx.clone(), resp) {
5769
toolbox.send(ctx.connection_id, resp);
5870
}

src/libs/ws/toolbox.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,28 @@ impl Display for CustomError {
5959

6060
impl std::error::Error for CustomError {}
6161

62+
/// Trait for handler-specific error enums.
63+
///
64+
/// Each variant defines its own error code and params string.
65+
/// Implementations provide their own `From<Self> for CustomError`.
66+
pub trait HandlerError: std::error::Error + Send + Sync + Into<CustomError> + 'static {
67+
/// Returns the error code for this variant.
68+
fn error_code(&self) -> ErrorCode;
69+
70+
/// Returns the params string for this variant.
71+
fn params(&self) -> String;
72+
}
73+
74+
impl HandlerError for CustomError {
75+
fn error_code(&self) -> ErrorCode {
76+
self.code
77+
}
78+
79+
fn params(&self) -> String {
80+
self.params.as_str().unwrap_or("").to_string()
81+
}
82+
}
83+
6284
#[derive(Clone)]
6385
pub struct RequestContext {
6486
pub connection_id: ConnectionId,

0 commit comments

Comments
 (0)