#![expect(
elided_lifetimes_in_paths,
reason = "divan's `#[bench]` macro only matches a `Bencher` with the lifetimes elided; spelling them out makes the signature invisible to it"
)]
#![expect(
clippy::panic,
reason = "benchmark inputs are consts; a decode failure is a broken benchmark, not a condition to average over"
)]
use std::hint::black_box;
use divan::Bencher;
use protocol_droid::adapters::secondary::a2a::A2aMethodTable;
use protocol_droid::application::jsonrpc::{RouteError, Routed, route_one};
use protocol_droid::domain::jsonrpc::{Envelope, Frame, decode};
use serde_json::value::RawValue;
const SEND_MESSAGE: &[u8] = br#"{"jsonrpc":"2.0","id":"a7f3-4c21-9e08","method":"SendMessage","params":{"message":{"role":"user","messageId":"m-1","parts":[{"kind":"text","text":"hello"}]}}}"#;
const GET_TASK: &[u8] = br#"{"jsonrpc":"2.0","id":1,"method":"GetTask","params":{"id":"t-1"}}"#;
const CANCEL_TASK: &[u8] =
br#"{"jsonrpc":"2.0","id":2,"method":"CancelTask","params":{"id":"t-2"}}"#;
const LIST_TASKS: &[u8] = br#"{"jsonrpc":"2.0","id":3,"method":"ListTasks","params":{"limit":50}}"#;
const SEND_STREAMING_MESSAGE: &[u8] = br#"{"jsonrpc":"2.0","id":4,"method":"SendStreamingMessage","params":{"message":{"role":"user","messageId":"m-2","parts":[{"kind":"text","text":"hi"}]}}}"#;
const SUBSCRIBE_TO_TASK: &[u8] =
br#"{"jsonrpc":"2.0","id":5,"method":"SubscribeToTask","params":{"id":"t-3"}}"#;
const UNROUTABLE: &[u8] = br#"{"jsonrpc":"2.0","id":1,"method":"NotARealMethod","params":{"message":{"role":"user","messageId":"m-1","parts":[{"kind":"text","text":"hello"}]}}}"#;
const REPLY: &[u8] =
br#"{"jsonrpc":"2.0","id":1,"result":{"kind":"task","id":"t-1","status":{"state":"working"}}}"#;
fn single(input: &[u8]) -> Envelope<'_> {
match decode(input) {
Ok(Frame::Single(envelope)) => envelope,
other => panic!("this benchmark input is one message, not {other:?}"),
}
}
fn route(b: Bencher, input: &[u8]) {
b.bench_local(|| {
let routed: Result<Routed<'_, _, Option<&RawValue>, Option<&RawValue>>, RouteError<'_>> =
route_one(single(black_box(input)), &A2aMethodTable::TABLE);
black_box(routed)
});
}
fn main() {
divan::main();
}
#[divan::bench]
fn decode_send_message(b: Bencher) {
b.bench_local(|| black_box(decode(black_box(SEND_MESSAGE))));
}
#[divan::bench]
fn decode_get_task(b: Bencher) {
b.bench_local(|| black_box(decode(black_box(GET_TASK))));
}
#[divan::bench]
fn decode_cancel_task(b: Bencher) {
b.bench_local(|| black_box(decode(black_box(CANCEL_TASK))));
}
#[divan::bench]
fn decode_list_tasks(b: Bencher) {
b.bench_local(|| black_box(decode(black_box(LIST_TASKS))));
}
#[divan::bench]
fn decode_send_streaming_message(b: Bencher) {
b.bench_local(|| black_box(decode(black_box(SEND_STREAMING_MESSAGE))));
}
#[divan::bench]
fn decode_subscribe_to_task(b: Bencher) {
b.bench_local(|| black_box(decode(black_box(SUBSCRIBE_TO_TASK))));
}
#[divan::bench]
fn decode_reply(b: Bencher) {
b.bench_local(|| black_box(decode(black_box(REPLY))));
}
#[divan::bench]
fn a_known_method_routes(b: Bencher) {
route(b, SEND_MESSAGE);
}
#[divan::bench]
fn an_unroutable_frame_never_reaches_the_params(b: Bencher) {
route(b, UNROUTABLE);
}
#[divan::bench]
fn a_get_task_routes(b: Bencher) {
route(b, GET_TASK);
}
#[divan::bench]
fn a_send_streaming_message_routes(b: Bencher) {
route(b, SEND_STREAMING_MESSAGE);
}
#[divan::bench]
fn a_reply_routes_to_no_method(b: Bencher) {
route(b, REPLY);
}