Skip to main content

Extractors

A type that implements FromContext becomes a handler parameter, resolved from the delivery before the body runs.

Every parameter of a #[subscriber] function after the message and the optional &mut Context is an extractor. The generated handler resolves the extractors in the order they are declared, then runs the body. from_context reads the context mutably, so an extractor can read headers, broker fields and the state. It is async, so it may also do I/O, such as a lookup. An error is the Rejection, which converts into a HandlerOutcome: the delivery settles with that outcome, and the body does not run. HandlerOutcome itself is a valid rejection; an extractor that cannot fail uses Infallible. A manual handler calls from_context at the top of handle and returns a rejection as its error. The impl is generic over the context type C and the state S, so the extractor works on any subscription. An extractor that reads a broker field or a typed state implements the trait for those types instead. State<T>, the extractor of the application state, and Ctx<K> are extractors of this kind that the crate provides.

Example​

/// The tenant an order belongs to, read off its `x-tenant` header.
struct Tenant(String);

impl<C: Send, S: Sync> FromContext<C, S> for Tenant {
type Rejection = HandlerOutcome;

fn from_context(
ctx: &mut Context<'_, C, S>,
) -> impl Future<Output = Result<Self, HandlerOutcome>> + Send {
let tenant = ctx.headers().get("x-tenant");
let tenant = tenant.map(|tenant| Self(String::from_utf8_lossy(tenant).into_owned()));
// No tenant, no work: the delivery is dropped and the body does not run.
ready(tenant.ok_or_else(HandlerOutcome::drop))
}
}

/// An accepted order and its tenant, published on `accepted`.
#[derive(Debug, PartialEq, Deserialize, Serialize, Outgoing)]
#[outgoing(name = "accepted")]
struct Accepted {
order: u64,
tenant: String,
}

/// The tenant arrives as a parameter, resolved before the body runs.
#[subscriber("orders", reply)]
async fn accept(order: &Order, Tenant(tenant): Tenant) -> Accepted {
Accepted {
order: order.id,
tenant,
}
}