yerpc/
lib.rs

1#![warn(clippy::wildcard_imports)]
2
3pub use async_trait::async_trait;
4use serde::{Deserialize, Serialize};
5
6pub use yerpc_derive::rpc;
7
8#[cfg(feature = "openrpc")]
9pub mod openrpc;
10mod requests;
11pub mod typescript;
12mod version;
13
14#[cfg(feature = "openrpc")]
15pub use openrpc::JsonSchema;
16pub use requests::{OutReceiver, RpcClient, RpcSession, RpcSessionSink};
17pub use typescript::TypeDef;
18pub use version::Version;
19
20mod integrations;
21pub use integrations::*;
22
23#[async_trait]
24pub trait RpcServer: Sync + Send + 'static {
25    /// Returns OpenRPC specification as a string.
26    #[cfg(feature = "openrpc")]
27    fn openrpc_specification() -> Result<String> {
28        Ok(String::new())
29    }
30
31    async fn handle_notification(&self, _method: String, _params: serde_json::Value) -> Result<()> {
32        Ok(())
33    }
34    async fn handle_request(
35        &self,
36        _method: String,
37        _params: serde_json::Value,
38    ) -> Result<serde_json::Value> {
39        Err(Error::new(
40            Error::METHOD_NOT_FOUND,
41            "Method not found".to_string(),
42        ))
43    }
44}
45
46impl RpcServer for () {}
47
48/// Request identifier as found in Request and Response objects.
49#[derive(Serialize, Deserialize, Debug, TypeDef, Eq, Hash, PartialEq, Clone)]
50#[serde(untagged)]
51pub enum Id {
52    Number(u32),
53    String(String),
54}
55
56pub type Result<T> = std::result::Result<T, Error>;
57
58/// Only used for generated TS bindings
59#[derive(Serialize, Deserialize, Debug, TypeDef)]
60#[serde(untagged)]
61pub enum RpcResult<T: TypeDef> {
62    Ok(T),
63    Err(Error),
64}
65
66#[derive(Serialize, Deserialize, Debug, TypeDef)]
67#[serde(untagged)]
68pub enum Message {
69    Request(Request),
70    Response(Response),
71}
72
73#[derive(Serialize, Deserialize, Debug, TypeDef)]
74#[serde(untagged)]
75pub enum Params {
76    Positional(Vec<serde_json::Value>),
77    Structured(serde_json::Map<String, serde_json::Value>),
78}
79
80impl Params {
81    pub fn into_value(self) -> serde_json::Value {
82        match self {
83            Params::Positional(list) => serde_json::Value::Array(list),
84            Params::Structured(object) => serde_json::Value::Object(object),
85        }
86    }
87}
88
89impl From<Params> for serde_json::Value {
90    fn from(params: Params) -> Self {
91        params.into_value()
92    }
93}
94
95impl TryFrom<serde_json::Value> for Params {
96    type Error = Error;
97    fn try_from(value: serde_json::Value) -> std::result::Result<Self, Self::Error> {
98        match value {
99            serde_json::Value::Object(object) => Ok(Params::Structured(object)),
100            serde_json::Value::Array(list) => Ok(Params::Positional(list)),
101            _ => Err(Error::invalid_params()),
102        }
103    }
104}
105
106/// Request object.
107#[derive(Serialize, Deserialize, Debug, TypeDef)]
108pub struct Request {
109    /// JSON-RPC protocol version.
110    #[serde(skip_serializing_if = "Option::is_none")]
111    pub jsonrpc: Option<Version>, // JSON-RPC 1.0 has no jsonrpc field
112
113    /// Name of the method to be invoked.
114    pub method: String,
115
116    /// Method parameters.
117    #[serde(skip_serializing_if = "Option::is_none")]
118    pub params: Option<Params>,
119
120    /// Request identifier.
121    #[serde(skip_serializing_if = "Option::is_none")]
122    pub id: Option<Id>,
123}
124
125/// Response object.
126#[derive(Serialize, Deserialize, Debug, TypeDef)]
127pub struct Response {
128    /// JSON-RPC protocol version.
129    pub jsonrpc: Version,
130
131    /// Request identifier.
132    pub id: Option<Id>,
133
134    /// Return value of the method.
135    #[serde(skip_serializing_if = "Option::is_none")]
136    pub result: Option<serde_json::Value>,
137
138    /// Error occured during the method invocation.
139    #[serde(skip_serializing_if = "Option::is_none")]
140    pub error: Option<Error>,
141}
142impl Response {
143    /// Creates a new Response object indicating an error.
144    pub fn error(id: Option<Id>, error: Error) -> Self {
145        Self {
146            jsonrpc: Version::V2,
147            id,
148            error: Some(error),
149            result: None,
150        }
151    }
152    /// Creates a new Response object indicating a success.
153    pub fn success(id: Id, result: serde_json::Value) -> Self {
154        Self {
155            jsonrpc: Version::V2,
156            id: Some(id),
157            result: Some(result),
158            error: None,
159        }
160    }
161}
162
163/// Error object returned in response to a failed RPC call.
164#[derive(Serialize, Deserialize, Debug, TypeDef)]
165#[cfg_attr(feature = "openrpc", derive(JsonSchema))]
166pub struct Error {
167    /// Error code indicating the error type.
168    pub code: i32,
169
170    /// Short error description.
171    pub message: String,
172
173    /// Additional information about the error.
174    #[serde(skip_serializing_if = "Option::is_none")]
175    pub data: Option<serde_json::Value>,
176}
177impl std::error::Error for Error {}
178impl std::fmt::Display for Error {
179    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
180        write!(f, "JSON-RPC error: {} (code {})", self.message, self.code)
181    }
182}
183impl Error {
184    pub const PARSE_ERROR: i32 = -32700;
185    pub const INVALID_REQUEST: i32 = -32600;
186    pub const METHOD_NOT_FOUND: i32 = -32601;
187    pub const INVALID_PARAMS: i32 = -32602;
188    pub const INTERNAL_ERROR: i32 = -32603;
189
190    pub const BAD_REQUEST: i32 = -32000;
191    pub const BAD_RESPONSE: i32 = -32001;
192    pub const REMOTE_DISCONNECTED: i32 = -32002;
193
194    /// Creates a new error object.
195    pub fn new(code: i32, message: String) -> Self {
196        Self {
197            code,
198            message,
199            data: None,
200        }
201    }
202
203    /// Creates a new error object with additional information.
204    pub fn with_data(code: i32, message: String, data: Option<serde_json::Value>) -> Self {
205        Self {
206            code,
207            message,
208            data,
209        }
210    }
211
212    /// Creates a new error object indicating invalid method parameters.
213    pub fn invalid_params() -> Self {
214        Self::new(
215            Error::INVALID_PARAMS,
216            "Params has to be an object or array".to_string(),
217        )
218    }
219
220    /// Creates a new error object indicating that the method does not exist.
221    pub fn method_not_found() -> Self {
222        Self::new(Error::METHOD_NOT_FOUND, "Method not found".to_string())
223    }
224
225    pub fn invalid_args_len(n: usize) -> Self {
226        Self::new(
227            Error::INVALID_PARAMS,
228            format!("This method takes an array of {n} arguments"),
229        )
230    }
231
232    pub fn bad_response() -> Self {
233        Self::new(
234            Error::BAD_RESPONSE,
235            "Error while processing a response".to_string(),
236        )
237    }
238    pub fn bad_request() -> Self {
239        Self::new(
240            Error::BAD_REQUEST,
241            "Error while serializing a request".to_string(),
242        )
243    }
244    pub fn remote_disconnected() -> Self {
245        Self::new(
246            Error::REMOTE_DISCONNECTED,
247            "Remote disconnected".to_string(),
248        )
249    }
250
251    pub fn is_disconnnected(&self) -> bool {
252        self.code == Error::REMOTE_DISCONNECTED
253    }
254}
255
256impl From<serde_json::Error> for Error {
257    fn from(error: serde_json::Error) -> Self {
258        Self {
259            code: Error::PARSE_ERROR,
260            message: format!("{error}"),
261            data: None,
262        }
263    }
264}
265
266#[cfg(feature = "anyhow")]
267#[cfg(feature = "anyhow_expose")]
268impl From<anyhow::Error> for Error {
269    fn from(error: anyhow::Error) -> Self {
270        Self {
271            code: -1,
272            message: format!("{:#}", error),
273            data: None,
274        }
275    }
276}
277
278#[cfg(feature = "anyhow")]
279#[cfg(not(feature = "anyhow_expose"))]
280impl From<anyhow::Error> for Error {
281    fn from(_error: anyhow::Error) -> Self {
282        Self {
283            code: Error::INTERNAL_ERROR,
284            message: "Internal server error".to_string(),
285            data: None,
286        }
287    }
288}