@@ -203,6 +203,12 @@ pub trait HandleDispatchFrom<Counterpart: Role>: Send {
203203 /// You should avoid blocking in this callback unless you wish to block the server (e.g., for rate limiting).
204204 /// The recommended approach to manage expensive operations is to the [`ConnectionTo::spawn`] method available on the message context.
205205 ///
206+ /// When implementing this directly, prefer [`Dispatch::match_request`],
207+ /// [`Dispatch::match_notification`], or [`Dispatch::match_typed_dispatch`]
208+ /// over manually calling `parse_message` and propagating the resulting error.
209+ /// These helpers preserve per-message failures as structured rejections instead
210+ /// of tearing down the entire connection.
211+ ///
206212 /// # Parameters
207213 ///
208214 /// * `message` - The incoming message to handle.
@@ -1786,7 +1792,10 @@ impl<Counterpart: Role> ConnectionTo<Counterpart> {
17861792 )
17871793 }
17881794
1789- /// Send an error notification (no reply expected).
1795+ /// Send an out-of-band JSON-RPC error message.
1796+ ///
1797+ /// This is serialized as a JSON-RPC error response with no `id`, so it is
1798+ /// not correlated with any specific request.
17901799 pub fn send_error_notification ( & self , error : crate :: Error ) -> Result < ( ) , crate :: Error > {
17911800 send_raw_message ( & self . message_tx , OutgoingMessage :: Error { error } )
17921801 }
@@ -2328,7 +2337,8 @@ impl<Req: JsonRpcRequest, Notif: JsonRpcMessage> Dispatch<Req, Notif> {
23282337 ///
23292338 /// If this message is a request, this error becomes the reply to the request.
23302339 ///
2331- /// If this message is a notification, the error is sent as a notification.
2340+ /// If this message is a notification, the error is sent as an out-of-band
2341+ /// JSON-RPC error message.
23322342 ///
23332343 /// If this message is a response, the error is forwarded to the waiting handler.
23342344 pub fn respond_with_error < R : Role > (
@@ -2343,24 +2353,23 @@ impl<Req: JsonRpcRequest, Notif: JsonRpcMessage> Dispatch<Req, Notif> {
23432353 }
23442354 }
23452355
2346- /// Reject a dispatch whose params failed to deserialize, without
2347- /// requiring a [`ConnectionTo`].
2356+ /// Handle a rejected typed match when no [`ConnectionTo`] is available.
23482357 ///
23492358 /// * **Requests** – sends the error back to the caller via the [`Responder`].
23502359 /// * **Responses** – forwards the error to the waiting handler via the
23512360 /// [`ResponseRouter`].
2352- /// * **Notifications** – there is no request ID to reply to, and no
2353- /// connection is available to send an error notification , so the error
2354- /// is logged and swallowed.
2361+ /// * **Notifications** – there is no request ID to reply to and no
2362+ /// connection available to send an out-of-band error message , so the
2363+ /// error is logged and swallowed.
23552364 ///
23562365 /// Returns `Ok(Handled::Yes)` in all cases so the connection loop
23572366 /// continues.
23582367 ///
23592368 /// **Prefer [`respond_with_error`](Self::respond_with_error)** when a
2360- /// [`ConnectionTo`] is available — it can send an error notification for
2369+ /// [`ConnectionTo`] is available — it can send an out-of-band error for
23612370 /// malformed notifications, which is consistent with
23622371 /// [`TypeNotification`](crate::util::TypeNotification).
2363- pub ( crate ) fn reject_parse_error (
2372+ pub ( crate ) fn handle_rejection_without_connection (
23642373 self ,
23652374 error : crate :: Error ,
23662375 ) -> Result < Handled < Dispatch > , crate :: Error > {
@@ -2369,11 +2378,9 @@ impl<Req: JsonRpcRequest, Notif: JsonRpcMessage> Dispatch<Req, Notif> {
23692378 responder. respond_with_error ( error) . map ( |( ) | Handled :: Yes )
23702379 }
23712380 Dispatch :: Notification ( _) => {
2372- // Notifications have no request ID, so there is no response
2373- // to send. Log and swallow to keep the connection alive.
23742381 tracing:: warn!(
23752382 ?error,
2376- "rejecting malformed notification: no response possible"
2383+ "rejecting malformed notification without connection : no out-of-band error possible"
23772384 ) ;
23782385 Ok ( Handled :: Yes )
23792386 }
@@ -2465,120 +2472,135 @@ fn normalize_incoming_message_parse_error(err: crate::Error) -> Option<crate::Er
24652472 }
24662473}
24672474
2468- pub ( crate ) enum TypedRequestOutcome < Req : JsonRpcRequest > {
2475+ /// Outcome of matching an untyped [`Dispatch`] against a request type.
2476+ #[ derive( Debug ) ]
2477+ pub enum RequestMatch < Req : JsonRpcRequest > {
2478+ /// The dispatch is a request whose method matched and whose params parsed successfully.
24692479 Matched ( Req , Responder < Req :: Response > ) ,
2480+ /// The dispatch was not a request of the requested type.
24702481 Unhandled ( Dispatch ) ,
2471- Reject {
2482+ /// The request method matched, but parsing failed.
2483+ Rejected {
2484+ /// The original untyped dispatch.
24722485 dispatch : Dispatch ,
2486+ /// The error explaining why the match was rejected.
24732487 error : crate :: Error ,
24742488 } ,
24752489}
24762490
2477- pub ( crate ) enum TypedNotificationOutcome < Notif : JsonRpcNotification > {
2491+ /// Outcome of matching an untyped [`Dispatch`] against a notification type.
2492+ #[ derive( Debug ) ]
2493+ pub enum NotificationMatch < Notif : JsonRpcNotification > {
2494+ /// The dispatch is a notification whose method matched and whose params parsed successfully.
24782495 Matched ( Notif ) ,
2496+ /// The dispatch was not a notification of the requested type.
24792497 Unhandled ( Dispatch ) ,
2480- Reject {
2498+ /// The notification method matched, but parsing failed.
2499+ Rejected {
2500+ /// The original untyped dispatch.
24812501 dispatch : Dispatch ,
2502+ /// The error explaining why the match was rejected.
24822503 error : crate :: Error ,
24832504 } ,
24842505}
24852506
2486- pub ( crate ) enum TypedDispatchOutcome < Req : JsonRpcRequest , Notif : JsonRpcNotification > {
2507+ /// Outcome of matching an untyped [`Dispatch`] against typed request and notification types.
2508+ #[ derive( Debug ) ]
2509+ pub enum TypedDispatchMatch < Req : JsonRpcRequest , Notif : JsonRpcNotification > {
2510+ /// The dispatch matched one of the provided types and parsed successfully.
24872511 Matched ( Dispatch < Req , Notif > ) ,
2512+ /// The dispatch did not match either provided type.
24882513 Unhandled ( Dispatch ) ,
2489- Reject {
2514+ /// The dispatch method matched, but parsing failed.
2515+ Rejected {
2516+ /// The original untyped dispatch.
24902517 dispatch : Dispatch ,
2518+ /// The error explaining why the match was rejected.
24912519 error : crate :: Error ,
24922520 } ,
24932521}
24942522
24952523impl Dispatch {
2496- pub ( crate ) fn classify_typed_request < Req : JsonRpcRequest > ( self ) -> TypedRequestOutcome < Req > {
2524+ /// Match this dispatch against a request type without using `Err` for parse failures.
2525+ #[ must_use]
2526+ pub fn match_request < Req : JsonRpcRequest > ( self ) -> RequestMatch < Req > {
24972527 match self {
24982528 Dispatch :: Request ( message, responder) => {
24992529 if Req :: matches_method ( & message. method ) {
25002530 match Req :: parse_message ( & message. method , & message. params ) {
2501- Ok ( req) => TypedRequestOutcome :: Matched ( req, responder. cast ( ) ) ,
2531+ Ok ( req) => RequestMatch :: Matched ( req, responder. cast ( ) ) ,
25022532 Err ( err) => match normalize_incoming_message_parse_error ( err) {
2503- Some ( error) => TypedRequestOutcome :: Reject {
2533+ Some ( error) => RequestMatch :: Rejected {
25042534 dispatch : Dispatch :: Request ( message, responder) ,
25052535 error,
25062536 } ,
2507- None => TypedRequestOutcome :: Unhandled ( Dispatch :: Request (
2508- message, responder,
2509- ) ) ,
2537+ None => RequestMatch :: Unhandled ( Dispatch :: Request ( message, responder) ) ,
25102538 } ,
25112539 }
25122540 } else {
2513- TypedRequestOutcome :: Unhandled ( Dispatch :: Request ( message, responder) )
2541+ RequestMatch :: Unhandled ( Dispatch :: Request ( message, responder) )
25142542 }
25152543 }
25162544 dispatch @ ( Dispatch :: Notification ( _) | Dispatch :: Response ( _, _) ) => {
2517- TypedRequestOutcome :: Unhandled ( dispatch)
2545+ RequestMatch :: Unhandled ( dispatch)
25182546 }
25192547 }
25202548 }
25212549
2522- pub ( crate ) fn classify_typed_notification < Notif : JsonRpcNotification > (
2523- self ,
2524- ) -> TypedNotificationOutcome < Notif > {
2550+ /// Match this dispatch against a notification type without using `Err` for parse failures.
2551+ # [ must_use ]
2552+ pub fn match_notification < Notif : JsonRpcNotification > ( self ) -> NotificationMatch < Notif > {
25252553 match self {
25262554 Dispatch :: Notification ( message) => {
25272555 if Notif :: matches_method ( & message. method ) {
25282556 match Notif :: parse_message ( & message. method , & message. params ) {
2529- Ok ( notif) => TypedNotificationOutcome :: Matched ( notif) ,
2557+ Ok ( notif) => NotificationMatch :: Matched ( notif) ,
25302558 Err ( err) => match normalize_incoming_message_parse_error ( err) {
2531- Some ( error) => TypedNotificationOutcome :: Reject {
2559+ Some ( error) => NotificationMatch :: Rejected {
25322560 dispatch : Dispatch :: Notification ( message) ,
25332561 error,
25342562 } ,
2535- None => {
2536- TypedNotificationOutcome :: Unhandled ( Dispatch :: Notification ( message) )
2537- }
2563+ None => NotificationMatch :: Unhandled ( Dispatch :: Notification ( message) ) ,
25382564 } ,
25392565 }
25402566 } else {
2541- TypedNotificationOutcome :: Unhandled ( Dispatch :: Notification ( message) )
2567+ NotificationMatch :: Unhandled ( Dispatch :: Notification ( message) )
25422568 }
25432569 }
25442570 dispatch @ ( Dispatch :: Request ( _, _) | Dispatch :: Response ( _, _) ) => {
2545- TypedNotificationOutcome :: Unhandled ( dispatch)
2571+ NotificationMatch :: Unhandled ( dispatch)
25462572 }
25472573 }
25482574 }
25492575
2550- pub ( crate ) fn classify_typed_dispatch < Req : JsonRpcRequest , Notif : JsonRpcNotification > (
2576+ /// Match this dispatch against typed request and notification types without using `Err`
2577+ /// for parse failures.
2578+ pub fn match_typed_dispatch < Req : JsonRpcRequest , Notif : JsonRpcNotification > (
25512579 self ,
2552- ) -> TypedDispatchOutcome < Req , Notif > {
2580+ ) -> TypedDispatchMatch < Req , Notif > {
25532581 match self {
2554- dispatch @ Dispatch :: Request ( _, _) => match dispatch. classify_typed_request :: < Req > ( ) {
2555- TypedRequestOutcome :: Matched ( request, responder) => {
2556- TypedDispatchOutcome :: Matched ( Dispatch :: Request ( request, responder) )
2557- }
2558- TypedRequestOutcome :: Unhandled ( dispatch) => {
2559- TypedDispatchOutcome :: Unhandled ( dispatch)
2582+ dispatch @ Dispatch :: Request ( _, _) => match dispatch. match_request :: < Req > ( ) {
2583+ RequestMatch :: Matched ( request, responder) => {
2584+ TypedDispatchMatch :: Matched ( Dispatch :: Request ( request, responder) )
25602585 }
2561- TypedRequestOutcome :: Reject { dispatch, error } => {
2562- TypedDispatchOutcome :: Reject { dispatch, error }
2586+ RequestMatch :: Unhandled ( dispatch) => TypedDispatchMatch :: Unhandled ( dispatch) ,
2587+ RequestMatch :: Rejected { dispatch, error } => {
2588+ TypedDispatchMatch :: Rejected { dispatch, error }
25632589 }
25642590 } ,
2565- dispatch @ Dispatch :: Notification ( _) => {
2566- match dispatch. classify_typed_notification :: < Notif > ( ) {
2567- TypedNotificationOutcome :: Matched ( notification) => {
2568- TypedDispatchOutcome :: Matched ( Dispatch :: Notification ( notification) )
2569- }
2570- TypedNotificationOutcome :: Unhandled ( dispatch) => {
2571- TypedDispatchOutcome :: Unhandled ( dispatch)
2572- }
2573- TypedNotificationOutcome :: Reject { dispatch, error } => {
2574- TypedDispatchOutcome :: Reject { dispatch, error }
2575- }
2591+ dispatch @ Dispatch :: Notification ( _) => match dispatch. match_notification :: < Notif > ( ) {
2592+ NotificationMatch :: Matched ( notification) => {
2593+ TypedDispatchMatch :: Matched ( Dispatch :: Notification ( notification) )
25762594 }
2577- }
2595+ NotificationMatch :: Unhandled ( dispatch) => TypedDispatchMatch :: Unhandled ( dispatch) ,
2596+ NotificationMatch :: Rejected { dispatch, error } => {
2597+ TypedDispatchMatch :: Rejected { dispatch, error }
2598+ }
2599+ } ,
25782600 Dispatch :: Response ( result, cx) => {
25792601 if !Req :: matches_method ( cx. method ( ) ) {
25802602 tracing:: trace!( "method doesn't match" ) ;
2581- return TypedDispatchOutcome :: Unhandled ( Dispatch :: Response ( result, cx) ) ;
2603+ return TypedDispatchMatch :: Unhandled ( Dispatch :: Response ( result, cx) ) ;
25822604 }
25832605 let typed_result = match result {
25842606 Ok ( value) => {
@@ -2593,7 +2615,7 @@ impl Dispatch {
25932615 }
25942616 Err ( err) => {
25952617 tracing:: trace!( ?err, "parse error" ) ;
2596- return TypedDispatchOutcome :: Reject {
2618+ return TypedDispatchMatch :: Rejected {
25972619 dispatch : Dispatch :: Response ( Ok ( value) , cx) ,
25982620 error : err,
25992621 } ;
@@ -2605,7 +2627,7 @@ impl Dispatch {
26052627 Err ( err)
26062628 }
26072629 } ;
2608- TypedDispatchOutcome :: Matched ( Dispatch :: Response ( typed_result, cx. cast ( ) ) )
2630+ TypedDispatchMatch :: Matched ( Dispatch :: Response ( typed_result, cx. cast ( ) ) )
26092631 }
26102632 }
26112633 }
@@ -2640,54 +2662,6 @@ impl Dispatch {
26402662 let session_id = serde_json:: from_value ( value. clone ( ) ) ?;
26412663 Ok ( Some ( session_id) )
26422664 }
2643-
2644- /// Try to parse this as a notification of the given type.
2645- ///
2646- /// # Returns
2647- ///
2648- /// * `Ok(Ok(typed))` if this is a request/notification of the given types
2649- /// * `Ok(Err(self))` if not
2650- /// * `Err` if has the correct method for the given types but parsing fails
2651- pub fn into_notification < N : JsonRpcNotification > (
2652- self ,
2653- ) -> Result < Result < N , Dispatch > , crate :: Error > {
2654- match self {
2655- Dispatch :: Notification ( msg) => {
2656- if !N :: matches_method ( & msg. method ) {
2657- return Ok ( Err ( Dispatch :: Notification ( msg) ) ) ;
2658- }
2659- match N :: parse_message ( & msg. method , & msg. params ) {
2660- Ok ( n) => Ok ( Ok ( n) ) ,
2661- Err ( err) => Err ( err) ,
2662- }
2663- }
2664- Dispatch :: Request ( ..) | Dispatch :: Response ( ..) => Ok ( Err ( self ) ) ,
2665- }
2666- }
2667-
2668- /// Try to parse this as a request of the given type.
2669- ///
2670- /// # Returns
2671- ///
2672- /// * `Ok(Ok(typed))` if this is a request/notification of the given types
2673- /// * `Ok(Err(self))` if not
2674- /// * `Err` if has the correct method for the given types but parsing fails
2675- pub fn into_request < Req : JsonRpcRequest > (
2676- self ,
2677- ) -> Result < Result < ( Req , Responder < Req :: Response > ) , Dispatch > , crate :: Error > {
2678- match self {
2679- Dispatch :: Request ( msg, responder) => {
2680- if !Req :: matches_method ( & msg. method ) {
2681- return Ok ( Err ( Dispatch :: Request ( msg, responder) ) ) ;
2682- }
2683- match Req :: parse_message ( & msg. method , & msg. params ) {
2684- Ok ( req) => Ok ( Ok ( ( req, responder. cast ( ) ) ) ) ,
2685- Err ( err) => Err ( err) ,
2686- }
2687- }
2688- Dispatch :: Notification ( ..) | Dispatch :: Response ( ..) => Ok ( Err ( self ) ) ,
2689- }
2690- }
26912665}
26922666
26932667impl < M : JsonRpcRequest + JsonRpcNotification > Dispatch < M , M > {
0 commit comments