use std::path::PathBuf; use anyhow::Result; use clap::Parser; use jsonrpsee::core::client::ClientT; use jsonrpsee::rpc_params; use reth_ipc::client::IpcClientBuilder; use serde_json::Value; use tracing_subscriber::EnvFilter; /// Simple IPC (Unix socket) JSON-RPC client for manual testing. /// /// Examples: /// - Call method without params: /// hero-client-unix --socket /tmp/baobab.ipc --method whoami /// /// - Call method with positional params (as JSON array): /// hero-client-unix --socket /tmp/baobab.ipc --method authenticate --params '["pubkey","signature","nonce"]' /// /// - Call method with single object param: /// hero-client-unix --socket /tmp/baobab.ipc --method create_job --params '{"job_id":"abc"}' #[derive(Parser, Debug)] #[command(name = "hero-client-unix", version, about = "IPC JSON-RPC client")] struct Args { /// Filesystem path to the Unix domain socket #[arg(long, default_value = "/tmp/baobab.ipc", env = "HERO_IPC_SOCKET")] socket: PathBuf, /// JSON-RPC method name to call #[arg(long)] method: String, /// JSON string for params. Either an array for positional params or an object for named params. /// Defaults to [] (no params). #[arg(long, default_value = "[]")] params: String, /// Log filter (e.g., info, debug, trace) #[arg(long, default_value = "info", env = "RUST_LOG")] log: String, } #[tokio::main] async fn main() -> Result<()> { let args = Args::parse(); tracing_subscriber::FmtSubscriber::builder() .with_env_filter(EnvFilter::new(args.log.clone())) .try_init() .expect("setting default subscriber failed"); let socket_str = args.socket.to_string_lossy().to_string(); let client = IpcClientBuilder::default().build(&socket_str).await?; let params_value: Value = serde_json::from_str(&args.params)?; // We deserialize responses to serde_json::Value for generality. // You can set a concrete type instead if needed. let result: Value = match params_value { Value::Array(arr) => match arr.len() { 0 => client.request(&args.method, rpc_params![]).await?, 1 => client.request(&args.method, rpc_params![arr[0].clone()]).await?, 2 => client.request(&args.method, rpc_params![arr[0].clone(), arr[1].clone()]).await?, 3 => client .request(&args.method, rpc_params![arr[0].clone(), arr[1].clone(), arr[2].clone()]) .await?, 4 => client .request( &args.method, rpc_params![arr[0].clone(), arr[1].clone(), arr[2].clone(), arr[3].clone()], ) .await?, 5 => client .request( &args.method, rpc_params![ arr[0].clone(), arr[1].clone(), arr[2].clone(), arr[3].clone(), arr[4].clone() ], ) .await?, 6 => client .request( &args.method, rpc_params![ arr[0].clone(), arr[1].clone(), arr[2].clone(), arr[3].clone(), arr[4].clone(), arr[5].clone() ], ) .await?, 7 => client .request( &args.method, rpc_params![ arr[0].clone(), arr[1].clone(), arr[2].clone(), arr[3].clone(), arr[4].clone(), arr[5].clone(), arr[6].clone() ], ) .await?, _ => { // Fallback: send entire array as a single param to avoid combinatorial explosion. // Adjust if your server expects strictly positional expansion beyond 7 items. client.request(&args.method, rpc_params![Value::Array(arr)]).await? } }, // Single non-array param (object, string, number, etc.) other => client.request(&args.method, rpc_params![other]).await?, }; println!("{}", serde_json::to_string_pretty(&result)?); Ok(()) }