Skip to main content

json_rpc/
lib.rs

1//! A JSON-RPC 2.0 implementation with a simple builder pattern.
2//!
3//! This library provides a simple, user-friendly API for creating JSON-RPC
4//! servers. It handles the JSON-RPC protocol layer including message parsing,
5//! method routing, and response generation. Methods are registered using a
6//! builder pattern with automatic parameter deserialization, making it easy to
7//! define handlers that accept typed parameters.
8//!
9//! # Design Goals
10//!
11//! The library prioritizes simplicity and usability for JSON-RPC servers.
12//! It uses a builder pattern (`Methods::new().add()`) for method registration
13//! and supports multiple transport implementations (stdio, in-memory, custom).
14//!
15//! # Architecture
16//!
17//! The library is organized into several modules:
18//!
19//! [`types`] contains JSON-RPC 2.0 message types including Request, Response,
20//! Notification, and Error. These structures handle serialization and
21//! deserialization of JSON-RPC messages.
22//!
23//! [`transports`] defines the Transport trait and provides implementations. The
24//! Stdio transport uses NDJSON (newline-delimited JSON) over stdin/stdout,
25//! while InMemory is useful for testing. Custom transports can be implemented
26//! by extending the Transport trait.
27//!
28//! [`methods`] contains the Methods type with a builder pattern API for
29//! registering JSON-RPC method handlers.
30//!
31//! [`error`] defines internal error types for implementation-level errors,
32//! separate from JSON-RPC protocol errors sent over the wire.
33//!
34//! # Quick Start
35//!
36//! Create a method registry and serve:
37//!
38//! ```no_run
39//! use json_rpc::Methods;
40//! use serde_json::Value;
41//!
42//! async fn echo(params: Value) -> Result<Value, json_rpc::Error> {
43//!     Ok(params)
44//! }
45//!
46//! let methods = Methods::new()
47//!     .add("echo", echo);
48//!
49//! # Ok::<(), json_rpc::Error>(())
50//! ```
51//!
52//! Run the server with the default Stdio transport:
53//!
54//! ```no_run
55//! use json_rpc::{Methods, Stdio};
56//! use serde_json::Value;
57//!
58//! async fn echo(params: Value) -> Result<Value, json_rpc::Error> {
59//!     Ok(params)
60//! }
61//!
62//! # tokio::runtime::Runtime::new().unwrap().block_on(async {
63//! let methods = Methods::new()
64//!     .add("echo", echo);
65//!
66//! let transport = Stdio::new();
67//! json_rpc::serve(transport, methods).await.unwrap();
68//! # Ok::<(), json_rpc::Error>(())
69//! # });
70//! ```
71//!
72//! # Struct Parameters
73//!
74//! Methods can use struct parameters for more complex APIs:
75//!
76//! ```no_run
77//! use json_rpc::{Methods, Stdio};
78//! use serde::Deserialize;
79//!
80//! # tokio::runtime::Runtime::new().unwrap().block_on(async {
81//! #[derive(Deserialize)]
82//! struct InitializeParams {
83//!     name: String,
84//!     version: String,
85//! }
86//!
87//! async fn initialize(params: InitializeParams) -> Result<String, json_rpc::Error> {
88//!     Ok(format!("Server {} v{} initialized", params.name, params.version))
89//! }
90//!
91//! # tokio::runtime::Runtime::new().unwrap().block_on(async {
92//! let methods = Methods::new()
93//!     .add("initialize", initialize);
94//! # json_rpc::serve(Stdio::new(), methods).await.unwrap();
95//! # });
96//! # });
97//! ```
98//!
99//! # Error Handling
100//!
101//! Methods return `Result<T, Error>`. Create JSON-RPC protocol errors with
102//! specific codes:
103//!
104//! ```no_run
105//! use json_rpc::{Methods, Error, Stdio};
106//! use serde_json::Value;
107//!
108//! async fn divide(params: (i32, i32)) -> Result<i32, Error> {
109//!     if params.1 == 0 {
110//!         return Err(Error::rpc(-32000, "Division by zero"));
111//!     }
112//!     Ok(params.0 / params.1)
113//! }
114//!
115//! # tokio::runtime::Runtime::new().unwrap().block_on(async {
116//! let methods = Methods::new()
117//!     .add("divide", divide);
118//! # json_rpc::serve(Stdio::new(), methods).await.unwrap();
119//! # });
120//! ```
121//!
122//! # Transports
123//!
124//! The library separates protocol handling from transport. The Stdio transport
125//! reads newline-delimited JSON from stdin and writes responses to stdout.
126//! The InMemory transport provides an in-memory channel for testing.
127//! Implement custom transports by implementing the Transport trait.
128
129pub use error::Error;
130pub use methods::Methods;
131pub use transports::{Http, InMemory, Stdio, Transport};
132pub use types::{Message, Notification, Request, RequestId, Response};
133
134/// Serve a JSON-RPC server with the given transport and methods.
135///
136/// This function creates a server with the provided methods and runs it
137/// using the specified transport. The transport determines how JSON-RPC
138/// messages are sent and received (e.g., stdio, TCP, in-memory).
139///
140/// Each transport implementation handles its own serving logic, allowing
141/// for different communication patterns (continuous stream, request/response, etc.).
142///
143/// # Example
144///
145/// ```no_run
146/// use json_rpc::{Methods, Stdio};
147/// use serde_json::Value;
148///
149/// async fn echo(params: Value) -> Result<Value, json_rpc::Error> {
150///     Ok(params)
151/// }
152///
153/// # tokio::runtime::Runtime::new().unwrap().block_on(async {
154/// let methods = Methods::new()
155///     .add("echo", echo);
156///
157/// let transport = Stdio::new();
158/// json_rpc::serve(transport, methods).await.unwrap();
159/// # });
160/// ```
161pub async fn serve<T>(transport: T, methods: Methods) -> Result<(), Error>
162where
163    T: Transport,
164{
165    transport.serve(methods).await
166}
167
168pub mod error;
169pub mod methods;
170pub mod transports;
171pub mod types;