use anyhow::Result;
use capnweb_client::{Client, ClientConfig};
use capnweb_core::CapId;
use serde_json::{json, Value};
use std::time::Instant;
use tracing::info;
async fn run_pipelined() -> Result<(Value, Value, Value, u128, usize)> {
let start = Instant::now();
let rpc_url =
std::env::var("RPC_URL").unwrap_or_else(|_| "http://localhost:3000/rpc/batch".to_string());
let config = ClientConfig {
url: rpc_url.clone(),
max_batch_size: 100,
timeout_ms: 30000,
};
let client = Client::new(config)?;
let mut batch = client.batch();
let user_result = batch.call(CapId::new(1), "authenticate", vec![json!("cookie-123")]);
let profile_result = batch.pipeline(
&user_result,
vec!["id"], "getUserProfile",
vec![], );
let notifications_result = batch.pipeline(
&user_result,
vec!["id"], "getNotifications",
vec![],
);
let results = batch.execute().await?;
let user = results.get(&user_result)?;
let profile = results.get(&profile_result)?;
let notifications = results.get(¬ifications_result)?;
let elapsed = start.elapsed().as_millis();
let request_count = 1;
Ok((user, profile, notifications, elapsed, request_count))
}
async fn run_sequential() -> Result<(Value, Value, Value, u128, usize)> {
let start = Instant::now();
let mut request_count = 0;
let rpc_url =
std::env::var("RPC_URL").unwrap_or_else(|_| "http://localhost:3000/rpc/batch".to_string());
let config = ClientConfig {
url: rpc_url.clone(),
max_batch_size: 100,
timeout_ms: 30000,
};
let client = Client::new(config)?;
request_count += 1;
let user = client
.call(CapId::new(1), "authenticate", vec![json!("cookie-123")])
.await?;
let user_id = user
.get("id")
.ok_or_else(|| anyhow::anyhow!("No user id in response"))?
.clone();
request_count += 1;
let profile = client
.call(CapId::new(1), "getUserProfile", vec![user_id.clone()])
.await?;
request_count += 1;
let notifications = client
.call(CapId::new(1), "getNotifications", vec![user_id])
.await?;
let elapsed = start.elapsed().as_millis();
Ok((user, profile, notifications, elapsed, request_count))
}
#[tokio::main]
async fn main() -> Result<()> {
tracing_subscriber::fmt()
.with_env_filter(
tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| "info".into()),
)
.init();
info!("🚀 Cap'n Web Rust Client - Batch Pipelining Example");
info!("================================================");
info!("");
info!("--- Running PIPELINED (batched, single round trip) ---");
match run_pipelined().await {
Ok((user_p, profile_p, notifs_p, elapsed_p, posts_p)) => {
info!("✅ Success!");
info!(" 📬 HTTP POSTs: {}", posts_p);
info!(" ⏱️ Time: {} ms", elapsed_p);
info!(" 👤 User: {}", serde_json::to_string_pretty(&user_p)?);
info!(
" 📝 Profile: {}",
serde_json::to_string_pretty(&profile_p)?
);
info!(
" 🔔 Notifications: {}",
serde_json::to_string_pretty(¬ifs_p)?
);
info!("");
info!("--- Running SEQUENTIAL (non-batched, multiple round trips) ---");
match run_sequential().await {
Ok((user_s, profile_s, notifs_s, elapsed_s, posts_s)) => {
info!("✅ Success!");
info!(" 📬 HTTP POSTs: {}", posts_s);
info!(" ⏱️ Time: {} ms", elapsed_s);
info!(" 👤 User: {}", serde_json::to_string_pretty(&user_s)?);
info!(
" 📝 Profile: {}",
serde_json::to_string_pretty(&profile_s)?
);
info!(
" 🔔 Notifications: {}",
serde_json::to_string_pretty(¬ifs_s)?
);
info!("");
info!("📊 Summary:");
info!(" Pipelined: {} POST, {} ms", posts_p, elapsed_p);
info!(" Sequential: {} POSTs, {} ms", posts_s, elapsed_s);
let speedup = elapsed_s as f64 / elapsed_p.max(1) as f64;
info!(
" 🎯 Performance improvement: {:.1}x faster with pipelining",
speedup
);
assert_eq!(user_p, user_s, "User results should match");
assert_eq!(profile_p, profile_s, "Profile results should match");
assert_eq!(notifs_p, notifs_s, "Notifications should match");
info!("");
info!("✅ All results validated successfully!");
}
Err(e) => {
info!("❌ Sequential execution failed: {}", e);
}
}
}
Err(e) => {
info!("❌ Pipelined execution failed: {}", e);
info!("Make sure the typescript_examples_server is running on port 3000");
}
}
Ok(())
}