ajj

Trait Handler

Source
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) -> Fut
  • async fn(HandlerCtx, Params) -> Fut
  • async fn(Params, S) -> Fut
  • async fn(HandlerCtx, Params) -> Fut
  • async 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§

Source

type Future: Future<Output = Box<RawValue>> + Send + 'static

The future returned by the handler.

Required Methods§

Source

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.

Implementors§

Source§

impl<F, Fut, Params, Payload, ErrData, S> Handler<(OutputResponsePayload, HandlerCtx, Params), S> for F
where F: FnOnce(HandlerCtx, Params) -> Fut + Clone + Send + Sync + 'static, Fut: Future<Output = ResponsePayload<Payload, ErrData>> + Send + 'static, Params: RpcRecv, Payload: RpcSend, ErrData: RpcSend,

Source§

type Future = Pin<Box<dyn Future<Output = Box<RawValue>> + Send>>

Source§

impl<F, Fut, Params, Payload, ErrData, S> Handler<(OutputResponsePayload, HandlerCtx, Params, S), S> for F
where F: FnOnce(HandlerCtx, Params, S) -> Fut + Clone + Send + Sync + 'static, Fut: Future<Output = ResponsePayload<Payload, ErrData>> + Send + 'static, Params: RpcRecv, Payload: RpcSend, ErrData: RpcSend, S: Send + Sync + 'static,

Source§

type Future = Pin<Box<dyn Future<Output = Box<RawValue>> + Send>>

Source§

impl<F, Fut, Params, Payload, ErrData, S> Handler<(OutputResponsePayload, Params), S> for F
where F: FnOnce(Params) -> Fut + Clone + Send + Sync + 'static, Fut: Future<Output = ResponsePayload<Payload, ErrData>> + Send + 'static, Params: RpcRecv, Payload: RpcSend, ErrData: RpcSend,

Source§

type Future = Pin<Box<dyn Future<Output = Box<RawValue>> + Send>>

Source§

impl<F, Fut, Params, Payload, ErrData, S> Handler<(OutputResponsePayload, Params, S), S> for F
where F: FnOnce(Params, S) -> Fut + Clone + Send + Sync + 'static, Fut: Future<Output = ResponsePayload<Payload, ErrData>> + Send + 'static, Params: RpcRecv, Payload: RpcSend, ErrData: RpcSend, S: Send + Sync + 'static,

Source§

type Future = Pin<Box<dyn Future<Output = Box<RawValue>> + Send>>

Source§

impl<F, Fut, Params, Payload, ErrData, S> Handler<(OutputResult, HandlerCtx, Params), S> for F
where F: FnOnce(HandlerCtx, Params) -> Fut + Clone + Send + Sync + 'static, Fut: Future<Output = Result<Payload, ErrData>> + Send + 'static, Params: RpcRecv, Payload: RpcSend, ErrData: RpcSend,

Source§

type Future = Pin<Box<dyn Future<Output = Box<RawValue>> + Send>>

Source§

impl<F, Fut, Params, Payload, ErrData, S> Handler<(OutputResult, HandlerCtx, Params, S), S> for F
where F: FnOnce(HandlerCtx, Params, S) -> Fut + Clone + Send + Sync + 'static, Fut: Future<Output = Result<Payload, ErrData>> + Send + 'static, Params: RpcRecv, Payload: RpcSend, ErrData: RpcSend, S: Send + Sync + 'static,

Source§

type Future = Pin<Box<dyn Future<Output = Box<RawValue>> + Send>>

Source§

impl<F, Fut, Params, Payload, ErrData, S> Handler<(OutputResult, Params), S> for F
where F: FnOnce(Params) -> Fut + Clone + Send + Sync + 'static, Fut: Future<Output = Result<Payload, ErrData>> + Send + 'static, Params: RpcRecv, Payload: RpcSend, ErrData: RpcSend,

Source§

type Future = Pin<Box<dyn Future<Output = Box<RawValue>> + Send>>

Source§

impl<F, Fut, Params, Payload, ErrData, S> Handler<(OutputResult, Params, S), S> for F
where F: FnOnce(Params, S) -> Fut + Clone + Send + Sync + 'static, Fut: Future<Output = Result<Payload, ErrData>> + Send + 'static, Params: RpcRecv, Payload: RpcSend, ErrData: RpcSend, S: Send + Sync + 'static,

Source§

type Future = Pin<Box<dyn Future<Output = Box<RawValue>> + Send>>

Source§

impl<F, Fut, Payload, ErrData, S> Handler<(OutputResponsePayload, HandlerCtx), S> for F
where F: FnOnce(HandlerCtx) -> Fut + Clone + Send + Sync + 'static, Fut: Future<Output = ResponsePayload<Payload, ErrData>> + Send + 'static, Payload: RpcSend, ErrData: RpcSend,

Source§

type Future = Pin<Box<dyn Future<Output = Box<RawValue>> + Send>>

Source§

impl<F, Fut, Payload, ErrData, S> Handler<(OutputResponsePayload, S, HandlerCtx), S> for F
where F: FnOnce(HandlerCtx, S) -> Fut + Clone + Send + Sync + 'static, Fut: Future<Output = ResponsePayload<Payload, ErrData>> + Send + 'static, Payload: RpcSend, ErrData: RpcSend, S: Send + Sync + 'static,

Source§

type Future = Pin<Box<dyn Future<Output = Box<RawValue>> + Send>>

Source§

impl<F, Fut, Payload, ErrData, S> Handler<(OutputResponsePayload,), S> for F
where F: FnOnce() -> Fut + Clone + Send + Sync + 'static, Fut: Future<Output = ResponsePayload<Payload, ErrData>> + Send + 'static, Payload: RpcSend, ErrData: RpcSend,

Source§

type Future = Pin<Box<dyn Future<Output = Box<RawValue>> + Send>>

Source§

impl<F, Fut, Payload, ErrData, S> Handler<(OutputResult, HandlerCtx), S> for F
where F: FnOnce(HandlerCtx) -> Fut + Clone + Send + Sync + 'static, Fut: Future<Output = Result<Payload, ErrData>> + Send + 'static, Payload: RpcSend, ErrData: RpcSend,

Source§

type Future = Pin<Box<dyn Future<Output = Box<RawValue>> + Send>>

Source§

impl<F, Fut, Payload, ErrData, S> Handler<(OutputResult, S, HandlerCtx), S> for F
where F: FnOnce(HandlerCtx, S) -> Fut + Clone + Send + Sync + 'static, Fut: Future<Output = Result<Payload, ErrData>> + Send + 'static, Payload: RpcSend, ErrData: RpcSend, S: Send + Sync + 'static,

Source§

type Future = Pin<Box<dyn Future<Output = Box<RawValue>> + Send>>

Source§

impl<F, Fut, Payload, ErrData, S> Handler<(OutputResult,), S> for F
where F: FnOnce() -> Fut + Clone + Send + Sync + 'static, Fut: Future<Output = Result<Payload, ErrData>> + Send + 'static, Payload: RpcSend, ErrData: RpcSend,

Source§

type Future = Pin<Box<dyn Future<Output = Box<RawValue>> + Send>>