Ниже показан английский оригинал.
Parameters
A destination declared as a name template becomes a channel whose address keeps its placeholders, each one declared as a parameter.
#[outgoing(name = "orders.{tenant}.accepted")] declares a name template.
The template is the channel's id and its address.
Each {placeholder} is an entry of the channel's parameters.
The document names the segments; what a service puts in them is run-time data and is not listed.
A type reaches the document where it is published: each type of an Out slot's #[publishes(..)] list gets a send operation on its channel.
A type with no declared destination contributes no channel, since each call site names its own.
Without the macros the slot's OutSlot::outgoing returns the list, and OutgoingMessageMetadata::with_parameters names the placeholders.
The example runs with cargo test --example asyncapi_reports --features macros,memory,asyncapi.
Example
- Macros
- Manual
/// An order accepted for one tenant: the tenant is a segment of the channel's name.
#[derive(Debug, Serialize, Outgoing, JsonSchema)]
#[outgoing(name = "orders.{tenant}.accepted")]
struct Accepted {
id: u64,
}
#[derive(OutSlot)]
#[publishes(Accepted)]
struct Tenants;
#[subscriber("orders")]
async fn accept(order: &Order, Out(tenants): Out<impl Publisher, Tenants>) -> HandlerOutcome {
let accepted = Accepted { id: order.id };
match tenants
.message(&accepted)
.to()
.tenant(&order.tenant)
.publish()
.await
{
Ok(()) => HandlerOutcome::ack(),
Err(_) => HandlerOutcome::retry(),
}
}
/// An order accepted for one tenant: the tenant is a segment of the channel's name.
#[derive(Debug, Serialize, JsonSchema)]
struct Accepted {
id: u64,
}
impl OutgoingDestination for Accepted {
type Form = NameTemplate;
const DESTINATION: &'static str = "orders.{tenant}.accepted";
const PARAMETERS: &'static [&'static str] = &["tenant"];
}
impl MessageHeaders for Accepted {
type Contract = NoHeaders;
}
impl<Cont> TemplateAddress<Cont> for Accepted {
type Builder = AcceptedAt<Cont, MissingSegment<Tenant>>;
fn begin(cont: Cont) -> Self::Builder {
AcceptedAt {
cont,
tenant: MissingSegment::new(),
}
}
}
/// The slot the handler publishes through, and the dictionary the document reports for it.
struct Tenants;
impl OutSlot for Tenants {
const NAME: &'static str = "Tenants";
type Destination = Reads;
fn outgoing() -> Vec<OutgoingMessageMetadata> {
<Accepted as OutMessages<Self>>::outgoing()
}
}
impl PublishedThrough<Tenants> for Accepted {}
impl<M: OutSlot> OutMessages<M> for Accepted {
fn outgoing() -> Vec<OutgoingMessageMetadata> {
vec![
OutgoingMessageMetadata::new(Self::DESTINATION, type_name::<Self>())
.with_parameters(Self::PARAMETERS)
.with_payload_schema(<Self as Documentable>::schema_json()),
]
}
}
struct Accept;
impl<O> Handle<Order, (), Outs<(O,)>> for Accept
where
O: OutEntry<Tenants, Wire: Publisher>,
{
async fn handle(
&self,
order: &Order,
outs: &Outs<(O,)>,
_ctx: &mut Context<'_>,
) -> Result<(), HandlerOutcome> {
let accepted = Accepted { id: order.id };
let tenants = outs.get(Tenants);
match tenants
.message(&accepted)
.to()
.tenant(&order.tenant)
.publish()
.await
{
Ok(()) => Ok(()),
Err(_) => Err(HandlerOutcome::retry()),
}
}
}