pub trait Handler<T, S>:
Clone
+ Send
+ Sync
+ Sized
+ 'static {
type Future: Future<Output = Box<RawValue>> + Send + 'static;
// Required method
fn call_with_state(self, args: HandlerArgs, state: S) -> Self::Future;
}Expand description
A trait describing handlers for JSON-RPC methods.
Handlers map some input type T to a future that resolve to a
ResponsePayload. The handler may also require some state S to operate.
§Returning error messages
Note that when a Handler returns a Result, the error type will be in
the ResponsePayload’s data field, and the message and code will
indicate an internal server error. To return a response with an error code
and custom message field, use a handler that returns an instance of the
ResponsePayload enum, and instantiate that error payload manually.
let handler_a = || async { Err::<(), _>("appears in \"data\"") };
let handler_b = || async {
ResponsePayload::<(), ()>::internal_error_message("appears in \"message\"".into())
};§Handler return type inference
Handlers that always suceed or always fail may have trouble with type inference, as they contain an unknown type parameter, which could be anything. Here’s an example of code with failed type inference:
// cannot infer type of the type parameter `T` declared on the enum `Result`
let cant_infer_ok = || async { Err(1) };
// cannot infer type of the type parameter `E` declared on the enum `Result`
let cant_infer_err = || async { Ok(2) };
// cannot infer type of the type parameter `ErrData` declared on the enum `ResponsePayload`
let cant_infer_failure = || async { ResponsePayload::Success(3) };
// cannot infer type of the type parameter `ErrData` declared on the enum `ResponsePayload`
let cant_infer_success = || async { ResponsePayload::internal_error_with_obj(4) };If you encounter these sorts of inference errors, you can add turbofish to your handlers’ return values like so:
// specify the Err on your Ok
let handler_a = || async { Ok::<_, ()>(1) };
// specify the Ok on your Err
let handler_b = || async { Err::<(), _>(2) };
// specify the ErrData on your Success
let handler_c = || async { ResponsePayload::Success::<_, ()>(3) };
// specify the Payload on your Failure
let handler_d = || async { ResponsePayload::<(), _>::internal_error_with_obj(4) };§Notifications
When running on pubsub, handlers can send notifications to the client. This
is done by calling HandlerCtx::notify. Notifications are sent as JSON
objects, and are queued for sending to the client. If the client is not
reading from the connection, the notification will be queued in a buffer.
If the buffer is full, the handler will be backpressured until the buffer
has room.
We recommend that handlers await on the result of HandlerCtx::notify,
to ensure that they are backpressured when the notification buffer is full.
If many tasks are attempting to notify the same client, the buffer may fill
up, and backpressure the RouteTask from reading requests from the
connection. This can lead to delays in request processing.
see the Listener documetnation for more information.
§Note on S
The S type parameter is “missing” state. It represents the state that the
handler needs to operate. This state is passed to the handler when calling
Handler::call_with_state.
§Blanket Implementations
This trait is blanket implemented for the following function and closure
types, where Fut is a Future returning either ResponsePayload or
Result:
async fn()async fn(Params) -> Futasync fn(HandlerCtx, Params) -> Futasync fn(Params, S) -> Futasync fn(HandlerCtx, Params) -> Futasync fn(HandlerCtx, Params, S) -> Fut
§Implementer’s note:
The generics on the implementation of the Handler trait are actually very
straightforward, even though they look intimidating.
The T type parameter is a marker and never needs be constructed.
It exists to differentiate the output impls, so that the trait can be
blanket implemented for many different function types. If it were simply
Handler<S>, there could only be 1 blanket impl.
However the T type must constrain the relevant input types. When
implementing Handler<T, S> for your own type, it is recommended to do the
following:
use ajj::{Handler, HandlerArgs, ResponsePayload, RawValue};
use std::{pin::Pin, future::Future};
/// A marker type to differentiate this handler from other blanket impls.
pub struct MyMarker {
_sealed: (),
}
#[derive(Clone)]
pub struct MyHandler;
// the `T` type parameter should be a tuple, containing your marker type,
// and the components that your handler actually uses. This ensures that
// the implementations are always unambiguous.
//
// e.g.
// - (MyMarker, )
// - (MyMarker, HandlerArgs)
// - (MyMarker, HandlerArgs, Params)
// etc.
impl<S> Handler<(MyMarker, ), S> for MyHandler {
type Future = Pin<Box<dyn Future<Output = Box<RawValue>> + Send>>;
fn call_with_state(self, _args: HandlerArgs, _state: S) -> Self::Future {
todo!("use nothing but your struct")
}
}
impl<S> Handler<(MyMarker, HandlerArgs), S> for MyHandler {
type Future = Pin<Box<dyn Future<Output = Box<RawValue>> + Send>>;
fn call_with_state(self, args: HandlerArgs, _state: S) -> Self::Future {
todo!("use the args")
}
}Required Associated Types§
Required Methods§
Sourcefn call_with_state(self, args: HandlerArgs, state: S) -> Self::Future
fn call_with_state(self, args: HandlerArgs, state: S) -> Self::Future
Call the handler with the given request and state.
Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.