use std::error::Error;
use std::future::{Future, ready};
use bytes::BytesMut;
use ruststream::codec::{CborCodec, Codec, CodecError, JsonCodec};
use ruststream::memory::MemoryPublisher;
use ruststream::memory::prelude::*;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use thiserror::Error;
const MAGIC: u8 = 0xE1;
const VERSION: u8 = 1;
#[derive(Debug, Clone, Copy, Default)]
struct Envelope<C> {
inner: C,
}
impl<C> Envelope<C> {
const fn new(inner: C) -> Self {
Self { inner }
}
}
impl<C: Codec> Codec for Envelope<C> {
fn encode<T: Serialize>(&self, value: &T) -> Result<BytesMut, CodecError> {
let payload = self.inner.encode(value)?;
let mut framed = BytesMut::with_capacity(2 + payload.len());
framed.extend_from_slice(&[MAGIC, VERSION]);
framed.extend_from_slice(&payload);
Ok(framed)
}
fn decode<T: DeserializeOwned>(&self, bytes: &[u8]) -> Result<T, CodecError> {
let payload = open(bytes).map_err(|err| CodecError::Decode(Box::new(err)))?;
self.inner.decode(payload)
}
}
fn open(bytes: &[u8]) -> Result<&[u8], EnvelopeError> {
match bytes {
[MAGIC, VERSION, payload @ ..] => Ok(payload),
[MAGIC, version, ..] => Err(EnvelopeError::Version(*version)),
[magic, _, ..] => Err(EnvelopeError::Magic(*magic)),
_ => Err(EnvelopeError::Truncated(bytes.len())),
}
}
#[derive(Debug, Error)]
enum EnvelopeError {
#[error("too short to carry an envelope header: {0} byte(s)")]
Truncated(usize),
#[error("not an envelope: leading byte {0:#04x}")]
Magic(u8),
#[error("envelope version {0} is not the version this build reads")]
Version(u8),
}
#[derive(Debug, Deserialize, schemars::JsonSchema)]
struct Order {
id: u64,
}
#[derive(Debug, Serialize)]
struct Receipt {
id: u64,
}
impl OutgoingDestination for Receipt {
type Form = CallerName;
}
impl MessageHeaders for Receipt {
type Contract = NoHeaders;
}
struct Receive;
impl Handle<Order> for Receive {
fn handle(
&self,
order: &Order,
_outs: &(),
_ctx: &mut Context<'_>,
) -> impl Future<Output = Result<(), HandlerOutcome>> {
println!("got order {}", order.id);
ready(Ok(()))
}
}
struct Audit;
impl Handle<Order> for Audit {
fn handle(
&self,
order: &Order,
_outs: &(),
_ctx: &mut Context<'_>,
) -> impl Future<Output = Result<(), HandlerOutcome>> {
println!("audited order {}", order.id);
ready(Ok(()))
}
}
struct Bill {
receipts: MemoryPublisher,
codec: Envelope<JsonCodec>,
}
impl Handle<Order> for Bill {
async fn handle(
&self,
order: &Order,
_outs: &(),
_ctx: &mut Context<'_>,
) -> Result<(), HandlerOutcome> {
if self
.receipts
.message(&Receipt { id: order.id })
.with_codec(self.codec)
.to("receipts")
.publish()
.await
.is_err()
{
return Err(HandlerOutcome::retry());
}
Ok(())
}
}
fn app() -> RustStream {
let info = AppInfo::new("custom-codec", "0.1.0");
RustStream::new(info).with_broker(MemoryBroker::new(), |b| {
b.include(
subscriber("orders", Receive)
.codec(Envelope::new(JsonCodec))
.build(),
);
b.include(
subscriber("audit", Audit)
.codec(Envelope::new(CborCodec))
.build(),
);
let receipts = b.broker().publisher();
b.include(
subscriber(
"billing",
Bill {
receipts,
codec: Envelope::new(JsonCodec),
},
)
.codec(Envelope::new(JsonCodec))
.build(),
);
})
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
app().run().await?;
Ok(())
}