use std::sync::Arc;
use anyhow::Result;
use axum::Router;
use axum::routing::post;
use json_rpc::axum::handler;
use json_rpc::{Error, JsonRpc};
use serde_json::Value;
use tracing::info;
async fn hello(params: String) -> Result<String, Error> {
info!("Hello called with params: {}", params);
if params == "world" {
Ok(format!("Hello, {}!", params))
} else {
Err(Error::rpc(-32000, "text must be 'world'"))
}
}
async fn internal_error(_params: Value) -> Result<String, Error> {
Err(Error::protocol("Internal error occurred"))
}
#[tokio::main]
async fn main() -> Result<()> {
tracing_subscriber::fmt()
.with_max_level(tracing::Level::DEBUG)
.with_writer(std::io::stderr)
.init();
info!("Initializing basic JSON-RPC server");
let json_rpc = JsonRpc::new()
.add("hello", hello)
.add("internal_error", internal_error);
let app = Router::new()
.route("/jsonrpc", post(handler))
.with_state(Arc::new(json_rpc));
let addr: std::net::SocketAddr = "127.0.0.1:3001".parse()?;
let listener = tokio::net::TcpListener::bind(addr).await?;
let local_addr = listener.local_addr()?;
info!("Server started on http://{}", local_addr);
info!("JSON-RPC endpoint: http://{}/jsonrpc", local_addr);
info!("Available methods:");
info!(" - hello: Returns greeting (params: \"world\")");
info!(" - internal_error: Returns internal error for testing");
info!("");
info!("Example request:");
info!(
" curl -X POST http://{}/jsonrpc -H \"Content-Type: application/json\" -d '{{\"jsonrpc\":\"2.0\",\"method\":\"hello\",\"params\":\"world\",\"id\":1}}'",
local_addr
);
axum::serve(listener, app).await?;
Ok(())
}