use dataflow_rs::{Engine, Message, Workflow};
use serde_json::{Value, json};
use std::sync::Arc;
use std::time::Instant;
const N: usize = 200_000;
const WARMUP: usize = 10_000;
const ITEMS: usize = 40;
fn subtotal() -> Value {
json!({
"reduce": [
{ "map": [ { "var": "data.input.items" }, { "*": [{ "var": "price" }, { "var": "qty" }] } ] },
{ "+": [{ "var": "current" }, { "var": "accumulator" }] },
0
]
})
}
#[tokio::main(flavor = "current_thread")]
async fn main() {
let workflow_json = json!({
"id": "checkout_workflow",
"name": "Checkout Aggregates Workflow",
"tasks": [
{
"id": "load_payload",
"name": "Parse Payload",
"function": {
"name": "parse_json",
"input": { "source": "payload", "target": "input" }
}
},
{
"id": "derive_cart",
"name": "Derive Cart Totals",
"function": {
"name": "map",
"input": {
"mappings": [
{ "path": "data.cart.subtotal", "logic": subtotal() },
{ "path": "data.cart.tax", "logic": { "*": [subtotal(), 0.08] } },
{ "path": "data.cart.shipping", "logic": { "if": [ { ">": [subtotal(), 100] }, 0, 9.99 ] } },
{ "path": "data.cart.discount", "logic": { "if": [ { ">": [subtotal(), 500] }, { "*": [subtotal(), 0.05] }, 0 ] } },
{ "path": "data.cart.total", "logic": { "-": [
{ "+": [
subtotal(),
{ "*": [subtotal(), 0.08] },
{ "if": [ { ">": [subtotal(), 100] }, 0, 9.99 ] }
] },
{ "if": [ { ">": [subtotal(), 500] }, { "*": [subtotal(), 0.05] }, 0 ] }
] } },
{ "path": "data.cart.item_count", "logic": {
"reduce": [
{ "map": [ { "var": "data.input.items" }, { "var": "qty" } ] },
{ "+": [{ "var": "current" }, { "var": "accumulator" }] },
0
]
} }
]
}
}
},
{
"id": "validate_cart",
"name": "Validate Cart Totals",
"function": {
"name": "validation",
"input": {
"rules": [
{ "path": "data.cart.subtotal", "logic": { ">": [{ "var": "data.cart.subtotal" }, 0] }, "message": "Subtotal must be positive" },
{ "path": "data.cart.total", "logic": { ">=": [{ "var": "data.cart.total" }, { "var": "data.cart.subtotal" }] }, "message": "Total must cover subtotal" }
]
}
}
}
]
});
let workflow = Workflow::from_json(&workflow_json.to_string()).unwrap();
let engine = Arc::new(Engine::builder().with_workflow(workflow).build().unwrap());
let items: Vec<Value> = (0..ITEMS)
.map(|i| {
json!({
"sku": format!("SKU-{i:04}"),
"price": 5.0 + (i as f64) * 1.25,
"qty": (i % 4) + 1
})
})
.collect();
let data = json!({ "items": items, "cart_id": "cart-42" });
{
let mut m = Message::from_value(&data);
engine.process_message(&mut m).await.unwrap();
let ctx = serde_json::to_value(m.data()).unwrap();
let subtotal = ctx["cart"]["subtotal"].as_f64().unwrap_or(0.0);
assert!(
subtotal > 0.0 && m.errors().is_empty(),
"workload is not computing: subtotal={subtotal}, errors={:?}",
m.errors()
);
}
for _ in 0..WARMUP {
let mut m = Message::from_value(&data);
engine.process_message(&mut m).await.unwrap();
}
let start = Instant::now();
for _ in 0..N {
let mut m = Message::from_value(&data);
engine.process_message(&mut m).await.unwrap();
}
let el = start.elapsed();
println!(
"AGGREGATE {} msgs ({} items, parse + 6 mappings + 2 validations) in {:.3}s => {:.1} ns/msg => {:.0} msg/s",
N,
ITEMS,
el.as_secs_f64(),
el.as_nanos() as f64 / N as f64,
N as f64 / el.as_secs_f64()
);
}