use bytes::BytesMut;
use ruststream::codec::{CborCodec, Codec, CodecError, JsonCodec};
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)]
struct Order {
id: u64,
}
#[derive(Debug, Serialize, Outgoing)]
struct Receipt {
id: u64,
}
#[subscriber("orders")]
async fn handle(order: &Order) -> HandlerOutcome {
println!("got order {}", order.id);
HandlerOutcome::ack()
}
#[subscriber("audit")]
async fn audit(order: &Order) -> HandlerOutcome {
println!("audited order {}", order.id);
HandlerOutcome::ack()
}
#[subscriber("billing", publish("receipts"))]
async fn bill(order: &Order) -> Receipt {
Receipt { id: order.id }
}
#[ruststream::app]
fn app() -> RustStream {
let info = AppInfo::new("custom-codec", "0.1.0");
RustStream::new(info).with_broker_codec(
MemoryBroker::new(),
Envelope::new(JsonCodec),
|b| {
b.include(handle);
b.include_router(
Router::new()
.with_codec(Envelope::new(CborCodec))
.include(audit),
);
b.include(bill)
.out_reply(Publish)
.codec(Envelope::new(JsonCodec));
},
)
}