Skip to content

Commit ebd50c4

Browse files
committed
Update naming for public interface
1 parent a577949 commit ebd50c4

5 files changed

Lines changed: 264 additions & 173 deletions

File tree

src/agent-client-protocol-core/src/jsonrpc.rs

Lines changed: 83 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -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

24952523
impl 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

26932667
impl<M: JsonRpcRequest + JsonRpcNotification> Dispatch<M, M> {

0 commit comments

Comments
 (0)