json_rpc/json_rpc.rs
1//! JSON-RPC 2.0 types and an async server.
2//!
3//! This crate provides pure data types for JSON-RPC 2.0 messages, plus a
4//! [`Server`] for method registration and dispatch. It is transport-agnostic
5//! and performs no I/O, so the caller drives the wire.
6//!
7//! # Features
8//!
9//! - Zero-copy deferred parsing via `serde_json::value::RawValue`
10//! - Generic `Server<C>` with typed async handler registration and automatic
11//! serde (de)serialization
12//! - Spec-compliant batch request/response handling, including invalid batch
13//! entries
14//! - Notification support
15//! - Owned context: `C` is cloned once per handler invocation (wrap it in an
16//! `Arc` for cheap clones)
17//!
18//! # Defining typed parameters and results
19//!
20//! ```rust
21//! use serde::{Deserialize, Serialize};
22//!
23//! #[derive(Deserialize)]
24//! struct AddParams {
25//! a: i64,
26//! b: i64,
27//! }
28//!
29//! #[derive(Serialize)]
30//! struct AddResult {
31//! sum: i64,
32//! }
33//! ```
34//!
35//! # Creating a server
36//!
37//! ```rust
38//! use json_rpc::{Error, Server, ServerConfig};
39//!
40//! #[derive(Clone)]
41//! struct AppState {
42//! prefix: String,
43//! }
44//!
45//! # use serde::{Deserialize, Serialize};
46//! # #[derive(Deserialize)]
47//! # struct AddParams {
48//! # a: i64,
49//! # b: i64,
50//! # }
51//! # #[derive(Serialize)]
52//! # struct AddResult {
53//! # sum: i64,
54//! # }
55//! let mut server: Server<AppState> = Server::new(ServerConfig::default());
56//!
57//! server.register("add", |ctx: AppState, params: AddParams| async move {
58//! Ok::<_, Error>(AddResult {
59//! sum: params.a + params.b,
60//! })
61//! });
62//! ```
63//!
64//! # Handling requests
65//!
66//! A single request:
67//!
68//! ```rust
69//! # use json_rpc::{Error, Request, RequestMessage, Server, ServerConfig};
70//! # #[tokio::main]
71//! # async fn main() {
72//! # let mut server = Server::<()>::new(ServerConfig::default());
73//! # server.register("add", |_: (), (a, b): (i64, i64)| async move { Ok::<_, Error>(a + b) });
74//! let json = r#"{"jsonrpc":"2.0","method":"add","params":[3,4],"id":1}"#;
75//! let request: Request = serde_json::from_str(json).unwrap();
76//!
77//! let response = server.handle((), RequestMessage::Single(request)).await;
78//! if let Some(response) = response {
79//! println!("{}", serde_json::to_string(&response).unwrap());
80//! // => {"jsonrpc":"2.0","result":7,"id":1}
81//! }
82//! # }
83//! ```
84//!
85//! A notification has no `id`, so [`Server::handle`] returns `None` and nothing
86//! should be sent back:
87//!
88//! ```rust
89//! # use json_rpc::{Error, Request, RequestMessage, Server, ServerConfig};
90//! # #[tokio::main]
91//! # async fn main() {
92//! # let mut server = Server::<()>::new(ServerConfig::default());
93//! # server.register("log", |_: (), _message: String| async move { Ok::<_, Error>(()) });
94//! let json = r#"{"jsonrpc":"2.0","method":"log","params":"hello"}"#;
95//! let request: Request = serde_json::from_str(json).unwrap();
96//!
97//! let response = server.handle((), RequestMessage::Single(request)).await;
98//! assert!(response.is_none());
99//! # }
100//! ```
101//!
102//! A batch:
103//!
104//! ```rust
105//! # use json_rpc::{Error, RequestMessage, Server, ServerConfig};
106//! # #[tokio::main]
107//! # async fn main() {
108//! # let mut server = Server::<()>::new(ServerConfig::default());
109//! # server.register("add", |_: (), (a, b): (i64, i64)| async move { Ok::<_, Error>(a + b) });
110//! let json = r#"[
111//! {"jsonrpc":"2.0","method":"add","params":[1,2],"id":"1"},
112//! {"jsonrpc":"2.0","method":"add","params":[3,4],"id":"2"}
113//! ]"#;
114//! let message: RequestMessage = serde_json::from_str(json).unwrap();
115//!
116//! let response = server.handle((), message).await;
117//! if let Some(response) = response {
118//! println!("{}", serde_json::to_string(&response).unwrap());
119//! }
120//! # }
121//! ```
122//!
123//! # Custom errors
124//!
125//! A handler may return any error type that converts into [`Error`]:
126//!
127//! ```rust
128//! # use json_rpc::{Error, Server, ServerConfig};
129//! # let mut server = Server::<()>::new(ServerConfig::default());
130//! #[derive(Debug)]
131//! enum AppError {
132//! DivisionByZero,
133//! Db(String),
134//! }
135//!
136//! impl From<AppError> for Error {
137//! fn from(e: AppError) -> Self {
138//! match e {
139//! AppError::DivisionByZero => Error::new(-32000, "division by zero"),
140//! AppError::Db(message) => Error::new(-32001, format!("internal: {message}")),
141//! }
142//! }
143//! }
144//!
145//! server.register("div", |_: (), (a, b): (i64, i64)| async move {
146//! if b == 0 {
147//! Err(AppError::DivisionByZero)
148//! } else {
149//! Ok(a / b)
150//! }
151//! });
152//! ```
153//!
154//! # Serving over a transport
155//!
156//! The server is transport-agnostic. For example, over a newline-delimited TCP
157//! stream:
158//!
159//! ```no_run
160//! use json_rpc::{Error, Id, RequestMessage, Response, Server, ServerConfig};
161//! use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
162//! use tokio::net::TcpListener;
163//!
164//! #[tokio::main]
165//! async fn main() -> std::io::Result<()> {
166//! let mut server = Server::<()>::new(ServerConfig::default());
167//! server.register("ping", |_: (), ()| async move { Ok::<_, Error>("pong".to_string()) });
168//!
169//! let listener = TcpListener::bind("127.0.0.1:8080").await?;
170//! loop {
171//! let (stream, _) = listener.accept().await?;
172//! let (reader, mut writer) = stream.into_split();
173//! let mut lines = BufReader::new(reader).lines();
174//!
175//! while let Ok(Some(line)) = lines.next_line().await {
176//! let message: RequestMessage = match serde_json::from_str(&line) {
177//! Ok(message) => message,
178//! Err(_) => {
179//! let error = Response::error(Id::Null, Error::parse_error());
180//! writer.write_all(serde_json::to_string(&error).unwrap().as_bytes()).await?;
181//! writer.write_all(b"\n").await?;
182//! continue;
183//! }
184//! };
185//!
186//! if let Some(response) = server.handle((), message).await {
187//! writer.write_all(serde_json::to_string(&response).unwrap().as_bytes()).await?;
188//! writer.write_all(b"\n").await?;
189//! }
190//! }
191//! }
192//! }
193//! ```
194//!
195//! Over HTTP, deserialize a [`RequestMessage`] from the request body, call
196//! [`Server::handle`], and serialize the returned [`ResponseMessage`] (or reply
197//! `204 No Content` when it is `None`):
198//!
199//! ```ignore
200//! use std::sync::Arc;
201//!
202//! use axum::{
203//! extract::State,
204//! extract::rejection::JsonRejection,
205//! http::StatusCode,
206//! response::IntoResponse,
207//! routing::post,
208//! Json, Router,
209//! };
210//! use json_rpc::{Error, Id, RequestMessage, Response, Server, ServerConfig};
211//!
212//! async fn rpc_handler(
213//! State(server): State<Arc<Server<AppState>>>,
214//! State(state): State<Arc<AppState>>,
215//! payload: Result<Json<RequestMessage>, JsonRejection>,
216//! ) -> impl IntoResponse {
217//! let message = match payload {
218//! Ok(Json(message)) => message,
219//! Err(_) => {
220//! let error = Response::error(Id::Null, Error::parse_error());
221//! return (StatusCode::OK, Json(error)).into_response();
222//! }
223//! };
224//!
225//! match server.handle((*state).clone(), message).await {
226//! Some(response) => (StatusCode::OK, Json(response)).into_response(),
227//! None => StatusCode::NO_CONTENT.into_response(),
228//! }
229//! }
230//! ```
231
232pub mod error_code;
233pub mod id;
234pub mod request;
235pub mod response;
236pub mod server;
237
238pub use error_code::ErrorCode;
239pub use id::Id;
240pub use request::{Request, RequestId, RequestMessage};
241pub use response::{Error, Response};
242pub use server::{ResponseMessage, Server, ServerConfig};