|
| 1 | +//! Handling incoming messages with `on_receive_*` callbacks. |
| 2 | +//! |
| 3 | +//! So far we've seen how to *send* messages. But ACP is bidirectional - the |
| 4 | +//! remote peer can also send messages to you. Use callbacks to handle them. |
| 5 | +//! |
| 6 | +//! # Handling Requests |
| 7 | +//! |
| 8 | +//! Use `on_receive_request` to handle incoming requests that expect a response: |
| 9 | +//! |
| 10 | +//! ``` |
| 11 | +//! # use sacp::{ClientToAgent, AgentToClient, Component}; |
| 12 | +//! # use sacp_test::{ValidateRequest, ValidateResponse}; |
| 13 | +//! # async fn example(transport: impl Component<AgentToClient>) -> Result<(), sacp::Error> { |
| 14 | +//! ClientToAgent::builder() |
| 15 | +//! .on_receive_request(async |req: ValidateRequest, request_cx, cx| { |
| 16 | +//! // Process the request |
| 17 | +//! let is_valid = req.data.len() > 0; |
| 18 | +//! |
| 19 | +//! // Send the response |
| 20 | +//! request_cx.respond(ValidateResponse { is_valid, error: None }) |
| 21 | +//! }, sacp::on_receive_request!()) |
| 22 | +//! .run_until(transport, async |cx| { Ok(()) }) |
| 23 | +//! .await?; |
| 24 | +//! # Ok(()) |
| 25 | +//! # } |
| 26 | +//! ``` |
| 27 | +//! |
| 28 | +//! Your callback receives three arguments: |
| 29 | +//! - The request payload (e.g., `PermissionRequest`) |
| 30 | +//! - A [`JrRequestCx`] for sending the response |
| 31 | +//! - A [`JrConnectionCx`] for sending other messages |
| 32 | +//! |
| 33 | +//! # Handling Notifications |
| 34 | +//! |
| 35 | +//! Use `on_receive_notification` for fire-and-forget messages that don't need |
| 36 | +//! a response: |
| 37 | +//! |
| 38 | +//! ``` |
| 39 | +//! # use sacp::{ClientToAgent, AgentToClient, Component}; |
| 40 | +//! # use sacp_test::StatusUpdate; |
| 41 | +//! # async fn example(transport: impl Component<AgentToClient>) -> Result<(), sacp::Error> { |
| 42 | +//! ClientToAgent::builder() |
| 43 | +//! .on_receive_notification(async |notif: StatusUpdate, cx| { |
| 44 | +//! println!("Status: {}", notif.message); |
| 45 | +//! Ok(()) |
| 46 | +//! }, sacp::on_receive_notification!()) |
| 47 | +//! # .run_until(transport, async |_| Ok(())).await?; |
| 48 | +//! # Ok(()) |
| 49 | +//! # } |
| 50 | +//! ``` |
| 51 | +//! |
| 52 | +//! # The Request Context |
| 53 | +//! |
| 54 | +//! The [`JrRequestCx`] lets you send a response to the request: |
| 55 | +//! |
| 56 | +//! ``` |
| 57 | +//! # use sacp::{ClientToAgent, AgentToClient, Component}; |
| 58 | +//! # use sacp_test::{MyRequest, MyResponse}; |
| 59 | +//! # async fn example(transport: impl Component<AgentToClient>) -> Result<(), sacp::Error> { |
| 60 | +//! # ClientToAgent::builder() |
| 61 | +//! # .on_receive_request(async |req: MyRequest, request_cx, cx| { |
| 62 | +//! // Send a successful response |
| 63 | +//! request_cx.respond(MyResponse { status: "ok".into() })?; |
| 64 | +//! # Ok(()) |
| 65 | +//! # }, sacp::on_receive_request!()) |
| 66 | +//! # .run_until(transport, async |_| Ok(())).await?; |
| 67 | +//! # Ok(()) |
| 68 | +//! # } |
| 69 | +//! ``` |
| 70 | +//! |
| 71 | +//! Or send an error: |
| 72 | +//! |
| 73 | +//! ``` |
| 74 | +//! # use sacp::{ClientToAgent, AgentToClient, Component}; |
| 75 | +//! # use sacp_test::{MyRequest, MyResponse}; |
| 76 | +//! # async fn example(transport: impl Component<AgentToClient>) -> Result<(), sacp::Error> { |
| 77 | +//! # ClientToAgent::builder() |
| 78 | +//! # .on_receive_request(async |req: MyRequest, request_cx, cx| { |
| 79 | +//! request_cx.respond_with_error(sacp::Error::invalid_params())?; |
| 80 | +//! # Ok(()) |
| 81 | +//! # }, sacp::on_receive_request!()) |
| 82 | +//! # .run_until(transport, async |_| Ok(())).await?; |
| 83 | +//! # Ok(()) |
| 84 | +//! # } |
| 85 | +//! ``` |
| 86 | +//! |
| 87 | +//! You must send exactly one response per request. If your callback returns |
| 88 | +//! without responding, an error response is sent automatically. |
| 89 | +//! |
| 90 | +//! # Multiple Handlers |
| 91 | +//! |
| 92 | +//! You can register multiple handlers. They're tried in order until one |
| 93 | +//! handles the message: |
| 94 | +//! |
| 95 | +//! ``` |
| 96 | +//! # use sacp::{ClientToAgent, AgentToClient, Component}; |
| 97 | +//! # use sacp_test::{ValidateRequest, ValidateResponse, ExecuteRequest, ExecuteResponse}; |
| 98 | +//! # async fn example(transport: impl Component<AgentToClient>) -> Result<(), sacp::Error> { |
| 99 | +//! ClientToAgent::builder() |
| 100 | +//! .on_receive_request(async |req: ValidateRequest, request_cx, cx| { |
| 101 | +//! // Handle validation requests |
| 102 | +//! request_cx.respond(ValidateResponse { is_valid: true, error: None }) |
| 103 | +//! }, sacp::on_receive_request!()) |
| 104 | +//! .on_receive_request(async |req: ExecuteRequest, request_cx, cx| { |
| 105 | +//! // Handle execution requests |
| 106 | +//! request_cx.respond(ExecuteResponse { result: "done".into() }) |
| 107 | +//! }, sacp::on_receive_request!()) |
| 108 | +//! # .run_until(transport, async |_| Ok(())).await?; |
| 109 | +//! # Ok(()) |
| 110 | +//! # } |
| 111 | +//! ``` |
| 112 | +//! |
| 113 | +//! # Ordering Guarantees |
| 114 | +//! |
| 115 | +//! Callbacks run inside the dispatch loop and block further message processing |
| 116 | +//! until they complete. This gives you ordering guarantees but also means you |
| 117 | +//! need to be careful about deadlocks. |
| 118 | +//! |
| 119 | +//! See [Ordering](super::ordering) for the full details. |
| 120 | +//! |
| 121 | +//! # Next Steps |
| 122 | +//! |
| 123 | +//! - [Explicit Peers](super::peers) - Use `_from` variants to specify the source peer |
| 124 | +//! - [Ordering](super::ordering) - Understand dispatch loop semantics |
| 125 | +//! |
| 126 | +//! [`JrRequestCx`]: crate::JrRequestCx |
| 127 | +//! [`JrConnectionCx`]: crate::JrConnectionCx |
0 commit comments