Ниже показан английский оригинал.
Destinations
A message type declares where it goes: a fixed name, a name template with a setter per placeholder, or no name, left to the call.
#[derive(Outgoing)] makes a type publishable, and #[outgoing(name = "..")] declares its destination.
A fixed name, "orders.confirmed", resolves the destination for every call site, so the publish needs no .to(..).
A name template, "orders.{tenant}.placed", opens .to() with one setter per placeholder, and the publish exists only once every placeholder is bound.
A template renders its name on each publish; a fixed name costs nothing per publish.
A type without a name takes .to("dest") at every call site.
A reply type declares a fixed name or none: the runtime publishes the reply and has nothing to bind a template's placeholders with, so a reply to a template does not compile.
The generated AsyncAPI document reports each declared destination, a template as a channel with parameters.
Without the macros, the type implements OutgoingDestination by hand: FixedName, NameTemplate or CallerName as its Form.
A template there also names the builder of its setters through TemplateAddress.
The same attribute declares the message's header contract, #[outgoing(headers = Meta)], described on the Outgoing page of typed headers.
Example
The three forms of a destination.
- Macros
- Manual
/// A fixed name: every publish of this type goes to `orders.confirmed`.
#[derive(Debug, PartialEq, Deserialize, Serialize, Outgoing)]
#[outgoing(name = "orders.confirmed")]
struct OrderConfirmed {
id: u64,
}
/// A name template: each placeholder becomes a setter on the publish builder.
#[derive(Debug, PartialEq, Deserialize, Serialize, Outgoing)]
#[outgoing(name = "orders.{tenant}.placed")]
struct OrderPlaced {
id: u64,
}
/// No name: the call site names the destination.
#[derive(Debug, PartialEq, Deserialize, Serialize, Outgoing)]
struct Notice {
id: u64,
}
/// A fixed name: every publish of this type goes to `orders.confirmed`.
#[derive(Debug, PartialEq, Deserialize, Serialize, schemars::JsonSchema)]
struct OrderConfirmed {
id: u64,
}
impl OutgoingDestination for OrderConfirmed {
type Form = FixedName;
const DESTINATION: &'static str = "orders.confirmed";
}
impl MessageHeaders for OrderConfirmed {
type Contract = NoHeaders;
}
/// A name template: the type names the builder that carries one setter per placeholder.
#[derive(Debug, PartialEq, Deserialize, Serialize, schemars::JsonSchema)]
struct OrderPlaced {
id: u64,
}
impl OutgoingDestination for OrderPlaced {
type Form = NameTemplate;
const DESTINATION: &'static str = "orders.{tenant}.placed";
const PARAMETERS: &'static [&'static str] = &["tenant"];
}
impl MessageHeaders for OrderPlaced {
type Contract = NoHeaders;
}
impl<Cont> TemplateAddress<Cont> for OrderPlaced {
type Builder = PlacedAt<Cont, MissingSegment<Tenant>>;
fn begin(cont: Cont) -> Self::Builder {
PlacedAt {
cont,
tenant: MissingSegment::new(),
}
}
}
/// No name: the call site names the destination.
#[derive(Debug, PartialEq, Deserialize, Serialize, schemars::JsonSchema)]
struct Notice {
id: u64,
}
impl OutgoingDestination for Notice {
type Form = CallerName;
}
impl MessageHeaders for Notice {
type Contract = NoHeaders;
}
Each publish fills only what its type leaves open.
- Macros
- Manual
#[subscriber("orders.incoming")]
async fn route(order: &Order, Out(orders): Out<impl Publisher, Orders>) -> HandlerOutcome {
let confirmed = orders
.message(&OrderConfirmed { id: order.id })
.publish()
.await;
let placed = orders
.message(&OrderPlaced { id: order.id })
.to()
.tenant(&order.tenant)
.publish()
.await;
let notice = orders
.message(&Notice { id: order.id })
.to("notices")
.publish()
.await;
if confirmed.is_err() || placed.is_err() || notice.is_err() {
return HandlerOutcome::retry();
}
HandlerOutcome::ack()
}
struct Route;
impl<O> Handle<Order, (), Outs<(O,)>> for Route
where
O: OutEntry<Orders, Wire: Publisher>,
{
async fn handle(
&self,
order: &Order,
outs: &Outs<(O,)>,
_ctx: &mut Context<'_>,
) -> Result<(), HandlerOutcome> {
let orders = outs.get(Orders);
let confirmed = orders
.message(&OrderConfirmed { id: order.id })
.publish()
.await;
let placed = orders
.message(&OrderPlaced { id: order.id })
.to()
.tenant(&order.tenant)
.publish()
.await;
let notice = orders
.message(&Notice { id: order.id })
.to("notices")
.publish()
.await;
if confirmed.is_err() || placed.is_err() || notice.is_err() {
return Err(HandlerOutcome::retry());
}
Ok(())
}
}