#![cfg_attr(not(test), allow(dead_code, unused_imports))]
use ruststream::memory::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Debug, PartialEq, Deserialize, Serialize, schemars::JsonSchema)]
struct Order {
id: u64,
}
impl OutgoingDestination for Order {
type Form = CallerName;
}
impl MessageHeaders for Order {
type Contract = NoHeaders;
}
mod hooks {
use std::convert::Infallible;
use std::error::Error;
use std::future::{Future, ready};
use std::sync::{Arc, Mutex};
use std::time::Duration;
use ruststream::memory::prelude::*;
use ruststream::testing::TestApp;
use super::Order;
struct Pool {
log: Arc<Mutex<Vec<&'static str>>>,
}
impl Pool {
fn open(log: Arc<Mutex<Vec<&'static str>>>) -> Self {
let pool = Self { log };
pool.record("open");
pool
}
fn record(&self, event: &'static str) {
if let Ok(mut log) = self.log.lock() {
log.push(event);
}
}
}
struct Accept;
impl Handle<Order, (), (), (), Pool> for Accept {
fn handle(
&self,
order: &Order,
_outs: &(),
_ctx: &mut Context<'_, (), Pool>,
) -> impl Future<Output = Result<(), HandlerOutcome>> {
tracing::info!(order.id, "accepted");
ready(Ok(()))
}
}
fn app(log: Arc<Mutex<Vec<&'static str>>>) -> impl App {
RustStream::new(AppInfo::new("orders", "0.1.0"))
.on_startup(async move |()| Ok::<_, Infallible>(Pool::open(log)))
.after_startup(async move |pool: Arc<Pool>| {
pool.record("ready");
Ok::<_, Infallible>(())
})
.on_shutdown(async move |pool: Arc<Pool>| {
pool.record("stopping");
Ok::<_, Infallible>(())
})
.after_shutdown(async move |pool: Arc<Pool>| {
pool.record("closed");
Ok::<_, Infallible>(())
})
.shutdown_timeout(Duration::from_secs(10))
.with_broker(MemoryBroker::new(), |b| {
b.include(subscriber("orders", Accept).build());
})
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn the_hooks_bracket_the_run() -> Result<(), Box<dyn Error>> {
let log = Arc::new(Mutex::new(Vec::new()));
let tb = TestApp::start(app(Arc::clone(&log))).await?;
assert_eq!(*log.lock().map_err(|e| e.to_string())?, ["open", "ready"]);
tb.shutdown().await?;
assert_eq!(
*log.lock().map_err(|e| e.to_string())?,
["open", "ready", "stopping", "closed"]
);
Ok(())
}
}
mod announce {
use std::error::Error;
use std::future::{Future, ready};
use ruststream::memory::prelude::*;
use ruststream::testing::TestApp;
use serde::{Deserialize, Serialize};
use super::Order;
#[derive(Debug, PartialEq, Deserialize, Serialize, schemars::JsonSchema)]
struct Started {
service: String,
}
impl OutgoingDestination for Started {
type Form = FixedName;
const DESTINATION: &'static str = "services.started";
}
impl MessageHeaders for Started {
type Contract = NoHeaders;
}
struct Accept;
impl Handle<Order> for Accept {
fn handle(
&self,
order: &Order,
_outs: &(),
_ctx: &mut Context<'_>,
) -> impl Future<Output = Result<(), HandlerOutcome>> {
tracing::info!(order.id, "accepted");
ready(Ok(()))
}
}
fn app() -> impl App {
RustStream::new(AppInfo::new("orders", "0.1.0")).with_broker(MemoryBroker::new(), |b| {
b.include(subscriber("orders", Accept).build());
b.after_startup(Publish, async move |publisher| {
let started = Started {
service: "orders".to_owned(),
};
publisher.message(&started).publish().await
});
})
}
#[tokio::test]
async fn the_service_announces_itself() -> Result<(), Box<dyn Error>> {
let tb = TestApp::start(app()).await?;
tb.broker::<MemoryBroker>()
.published::<Started>("services.started")
.assert_called_once()
.with(&Started {
service: "orders".to_owned(),
});
Ok(())
}
}
fn main() {}