use std::error::Error;
use std::fmt::Display;
use std::future::{Future, ready};
use ruststream::memory::prelude::*;
use ruststream::runtime::{
BoundSegment, ContextKind, MissingSegment, PublishAt, PublishError, PublishLayer, PublishNext,
PublishPipeline, PublishTransform, Reads, TemplateAddress,
};
use ruststream::runtime::Outgoing;
use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize, schemars::JsonSchema)]
struct Request {
id: u64,
}
#[derive(Debug, Serialize, schemars::JsonSchema)]
struct Response {
ok: bool,
}
impl OutgoingDestination for Response {
type Form = CallerName;
}
impl MessageHeaders for Response {
type Contract = NoHeaders;
}
#[derive(Debug, Deserialize, Serialize, schemars::JsonSchema)]
struct Event {
id: u64,
}
impl OutgoingDestination for Event {
type Form = CallerName;
}
impl MessageHeaders for Event {
type Contract = NoHeaders;
}
impl<M: OutSlot> OutMessages<M> for Event {
fn outgoing() -> Vec<OutgoingMessageMetadata> {
Vec::new()
}
}
#[derive(Debug, Serialize, schemars::JsonSchema)]
struct Receipt {
id: u64,
}
impl OutgoingDestination for Receipt {
type Form = FixedName;
const DESTINATION: &'static str = "receipts";
}
impl MessageHeaders for Receipt {
type Contract = NoHeaders;
}
struct IssueReceipt;
impl Handle<Request, Receipt> for IssueReceipt {
fn handle(
&self,
req: &Request,
_outs: &(),
_ctx: &mut Context<'_>,
) -> impl Future<Output = Result<Receipt, HandlerOutcome>> {
ready(Ok(Receipt { id: req.id }))
}
}
struct Respond;
impl Handle<Request, Response> for Respond {
fn handle(
&self,
req: &Request,
_outs: &(),
_ctx: &mut Context<'_>,
) -> impl Future<Output = Result<Response, HandlerOutcome>> {
println!("responding to request {}", req.id);
ready(Ok(Response { ok: true }))
}
}
struct Validate;
impl Handle<Request, Response> for Validate {
fn handle(
&self,
req: &Request,
_outs: &(),
_ctx: &mut Context<'_>,
) -> impl Future<Output = Result<Response, HandlerOutcome>> {
if req.id == 0 {
return ready(Err(HandlerOutcome::drop()));
}
ready(Ok(Response { ok: true }))
}
}
struct Forward;
impl<Egress> Handle<Event, (), Outs<(Egress,)>> for Forward
where
Egress: OutEntry<DefaultSlot, Wire: Publisher>,
{
async fn handle(
&self,
event: &Event,
outs: &Outs<(Egress,)>,
_ctx: &mut Context<'_>,
) -> Result<(), HandlerOutcome> {
if outs
.get(DefaultSlot)
.message(event)
.to("egress")
.publish()
.await
.is_err()
{
return Err(HandlerOutcome::retry());
}
Ok(())
}
}
struct Primary;
impl OutSlot for Primary {
const NAME: &'static str = "Primary";
type Destination = Reads;
fn outgoing() -> Vec<OutgoingMessageMetadata> {
<Event as OutMessages<Self>>::outgoing()
}
}
impl PublishedThrough<Primary> for Event {}
struct Shadow;
impl OutSlot for Shadow {
const NAME: &'static str = "Shadow";
type Destination = Reads;
fn outgoing() -> Vec<OutgoingMessageMetadata> {
<Event as OutMessages<Self>>::outgoing()
}
}
impl PublishedThrough<Shadow> for Event {}
struct Mirror;
impl<P, S> Handle<Event, (), Outs<(P, S)>> for Mirror
where
P: OutEntry<Primary, Wire: Publisher>,
S: OutEntry<Shadow, Wire: Publisher>,
{
async fn handle(
&self,
event: &Event,
outs: &Outs<(P, S)>,
_ctx: &mut Context<'_>,
) -> Result<(), HandlerOutcome> {
if outs
.get(Primary)
.message(event)
.to("mirror-primary")
.publish()
.await
.is_err()
|| outs
.get(Shadow)
.message(event)
.to("mirror-shadow")
.publish()
.await
.is_err()
{
return Err(HandlerOutcome::retry());
}
Ok(())
}
}
struct Gateway;
impl<Audit> Handle<Request, Response, Outs<(Audit,)>> for Gateway
where
Audit: OutEntry<DefaultSlot, Wire: Publisher>,
{
async fn handle(
&self,
req: &Request,
outs: &Outs<(Audit,)>,
_ctx: &mut Context<'_>,
) -> Result<Response, HandlerOutcome> {
if outs
.get(DefaultSlot)
.message(&Event { id: req.id })
.to("gateway-audit")
.publish()
.await
.is_err()
{
return Err(HandlerOutcome::retry());
}
Ok(Response { ok: true })
}
}
#[derive(Debug, Serialize)]
struct OrderConfirmed {
id: u64,
}
impl OutgoingDestination for OrderConfirmed {
type Form = FixedName;
const DESTINATION: &'static str = "orders.confirmed";
}
impl MessageHeaders for OrderConfirmed {
type Contract = NoHeaders;
}
impl<M: OutSlot> OutMessages<M> for OrderConfirmed {
fn outgoing() -> Vec<OutgoingMessageMetadata> {
vec![OutgoingMessageMetadata::new(
Self::DESTINATION,
std::any::type_name::<Self>(),
)]
}
}
#[derive(Debug, Serialize)]
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<M: OutSlot> OutMessages<M> for OrderPlaced {
fn outgoing() -> Vec<OutgoingMessageMetadata> {
vec![
OutgoingMessageMetadata::new(Self::DESTINATION, std::any::type_name::<Self>())
.with_parameters(Self::PARAMETERS),
]
}
}
#[derive(Debug, Clone, Copy)]
struct Tenant;
#[must_use = "an address builder does nothing until publish() is awaited"]
struct PlacedAt<Cont, S0> {
cont: Cont,
segment_0: S0,
}
impl<Cont> PlacedAt<Cont, MissingSegment<Tenant>> {
fn start(cont: Cont) -> Self {
Self {
cont,
segment_0: MissingSegment::new(),
}
}
fn tenant(self, value: impl Display) -> PlacedAt<Cont, String> {
PlacedAt {
cont: self.cont,
segment_0: value.to_string(),
}
}
}
impl<Cont, S0> PlacedAt<Cont, S0> {
async fn publish(self) -> Result<(), PublishError<<Cont as PublishAt>::Error>>
where
Cont: PublishAt,
S0: BoundSegment,
{
let address = format!("orders.{}.placed", self.segment_0);
PublishAt::publish_at(self.cont, &address).await
}
}
impl<Cont> TemplateAddress<Cont> for OrderPlaced {
type Builder = PlacedAt<Cont, MissingSegment<Tenant>>;
fn begin(cont: Cont) -> Self::Builder {
PlacedAt::start(cont)
}
}
struct Orders;
impl OutSlot for Orders {
const NAME: &'static str = "Orders";
type Destination = Reads;
fn outgoing() -> Vec<OutgoingMessageMetadata> {
let mut declared = <OrderConfirmed as OutMessages<Self>>::outgoing();
declared.extend(<OrderPlaced as OutMessages<Self>>::outgoing());
declared
}
}
impl PublishedThrough<Orders> for OrderConfirmed {}
impl PublishedThrough<Orders> for OrderPlaced {}
struct Route;
impl<O> Handle<Event, (), Outs<(O,)>> for Route
where
O: OutEntry<Orders, Wire: Publisher>,
{
async fn handle(
&self,
event: &Event,
outs: &Outs<(O,)>,
_ctx: &mut Context<'_>,
) -> Result<(), HandlerOutcome> {
let orders = outs.get(Orders);
if orders
.message(&OrderConfirmed { id: event.id })
.publish()
.await
.is_err()
{
return Err(HandlerOutcome::retry());
}
if orders
.message(&OrderPlaced { id: event.id })
.to()
.tenant("acme")
.publish()
.await
.is_err()
{
return Err(HandlerOutcome::retry());
}
Ok(())
}
}
struct EnvelopeTransform;
impl<K: ContextKind, Options> PublishTransform<K, Options> for EnvelopeTransform {
type Destination = Reads;
fn apply(&self, out: &mut Outgoing<'_>, _options: &mut Option<Options>, _cx: &K::View<'_>) {
out.headers_mut().insert("x-envelope", b"1".to_vec());
}
}
struct OutboxEnvelope;
impl<K: ContextKind, Options> PublishTransform<K, Options> for OutboxEnvelope {
type Destination = Reads;
fn apply(&self, out: &mut Outgoing<'_>, _options: &mut Option<Options>, _cx: &K::View<'_>) {
out.headers_mut().insert("x-outbox", b"1".to_vec());
}
}
#[derive(Clone)]
struct AuditPublish;
impl PublishLayer for AuditPublish {
async fn on_publish<'a, N: PublishPipeline, P: Publisher>(
&'a self,
out: &'a mut Outgoing<'a>,
next: PublishNext<'a, N, P>,
) -> Result<(), Box<dyn Error + Send + Sync>> {
println!("publishing to {}", out.name());
next.run(out).await
}
}
struct Confirm;
impl Handle<[Event], Vec<Event>> for Confirm {
fn handle(
&self,
orders: &[Event],
_outs: &(),
_ctx: &mut Context<'_>,
) -> impl Future<Output = Result<Vec<Event>, Vec<HandlerOutcome>>> {
if orders.iter().any(|o| o.id == 0) {
return ready(Err(orders.iter().map(|_| HandlerOutcome::drop()).collect()));
}
ready(Ok(orders.iter().map(|o| Event { id: o.id }).collect()))
}
}
async fn seed_events<P>(seeder: P) -> Result<(), Box<dyn Error + Send + Sync>>
where
P: TransactionalPublisher,
{
let scope = seeder.begin().await?;
scope
.message(&Event { id: 1 })
.to("events")
.publish()
.await?;
scope
.message(&Event { id: 2 })
.to("events")
.publish()
.await?;
scope.commit().await?;
Ok(())
}
fn app() -> impl App {
let broker = MemoryBroker::new();
RustStream::new(AppInfo::new("publishing", "0.1.0"))
.publish_layer(AuditPublish)
.with_broker(broker, |b| {
b.after_startup(TransactionalPublish, async move |seeder| {
seed_events(seeder).await.map_err(std::io::Error::other)
});
b.include(subscriber("receipt-requests", IssueReceipt).reply().build());
b.include(
subscriber("requests", Respond)
.reply()
.to("responses")
.build(),
)
.out_reply(Publish)
.transform(EnvelopeTransform);
b.include(
subscriber("validated-requests", Validate)
.reply()
.to("responses")
.build(),
);
b.include(subscriber("ingress", Forward).build())
.out(DefaultSlot, Publish)
.build();
b.include(subscriber("mirror", Mirror).build())
.out(Shadow, Publish)
.transform(OutboxEnvelope)
.out(Primary, Publish)
.build();
b.include(
subscriber("gateway-requests", Gateway)
.reply()
.to("gateway-responses")
.build(),
)
.out(DefaultSlot, Publish)
.build();
b.include(subscriber("orders.incoming", Route).build())
.out(Orders, Publish)
.build();
b.include(
subscriber("orders", Confirm)
.reply()
.to("confirmations")
.batch(nonzero!(64))
.build(),
)
.out_reply(TransactionalPublish)
.transactional();
})
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
app().run().await?;
Ok(())
}