1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
#[doc = include_str!("../README.md")]
mod utils;
pub use utils::*;

use std::sync::Arc;

use async_trait::async_trait;
pub use nanorpc_derive::nanorpc_derive;
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(untagged)]
/// A raw, JSON-RPC request ID. This should usually never be manually constructed.
pub enum JrpcId {
    Number(i64),
    String(String),
}

#[derive(Serialize, Deserialize, Clone, Debug)]
/// A raw, JSON-RPC request. This should usually never be manually constructed.
pub struct JrpcRequest {
    pub jsonrpc: String,
    pub method: String,
    pub params: Vec<serde_json::Value>,
    pub id: JrpcId,
}

#[derive(Serialize, Deserialize, Clone, Debug)]
/// A raw, JSON-RPC response. This should usually never be manually constructed.
pub struct JrpcResponse {
    pub jsonrpc: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(default)]
    pub result: Option<serde_json::Value>,
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(default)]
    pub error: Option<JrpcError>,
    pub id: JrpcId,
}

#[derive(Serialize, Deserialize, Clone, Debug)]
/// A raw, JSON-RPC error. This should usually never be manually constructed.
pub struct JrpcError {
    code: i64,
    message: String,
    data: serde_json::Value,
}

/// A server-returned error message. Contains a string description as well as a structured value.
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
pub struct ServerError {
    pub code: u32,
    pub message: String,
    pub details: serde_json::Value,
}

/// A struct implementing the [`RpcService`] represents the *server-side* logic of a NanoRPC. The method that needs to be *implemented* is [`RpcService::respond`], but actual servers would typically call [`RpcService::respond_raw`].
///
/// This trait uses the [`::async_trait`] crate, so the autogenerated documentation has somewhat inscrutable function signatures. [`RpcService`] has this "actual" definition:
///
/// ```
/// use nanorpc::{ServerError, JrpcRequest, JrpcResponse};
///
/// #[async_trait::async_trait]
/// pub trait RpcService {
///     async fn respond(
///         &self,
///         method: &str,
///         params: Vec<serde_json::Value>,
///     ) -> Option<Result<serde_json::Value, ServerError>>;
///
///     async fn respond_raw(&self, jrpc_req: JrpcRequest) -> JrpcResponse;
/// }
/// ```
///
///
/// # Examples
///
/// ## Using an RpcService to respond to client requests
///
/// ```
/// use nanorpc::{RpcService, ServerError, JrpcRequest, JrpcResponse};
///
/// /// Object that implements the business logic
/// struct BusinessLogic;
///
/// #[async_trait::async_trait]
/// impl RpcService for BusinessLogic {
///     async fn respond(&self,
///         method: &str,
///         params: Vec<serde_json::Value>
///     ) -> Option<Result<serde_json::Value, ServerError>> {
///         // business logic here
///         todo!()
///     }
/// }
///
/// /// Return the global BusinessLogic struct
/// fn bizlogic_singleton() -> &'static BusinessLogic { todo!() }
///
/// /// Handle a raw JSON-RPC request from, say, HTTP or TCP, returning the raw request
/// async fn handle_request(request: &[u8]) -> anyhow::Result<Vec<u8>> {
///     let request: JrpcRequest = serde_json::from_slice(request)?;
///     let response: JrpcResponse = bizlogic_singleton().respond_raw(request).await;
///     Ok(serde_json::to_vec(&response).unwrap())
/// }
#[async_trait]
pub trait RpcService: Sync + Send + 'static {
    /// Responds to an RPC call with method `str` and dynamically typed arguments `args`. The service should return `None` to indicate that this method does not exist at all.
    async fn respond(
        &self,
        method: &str,
        params: Vec<serde_json::Value>,
    ) -> Option<Result<serde_json::Value, ServerError>>;

    /// Responds to a raw JSON-RPC request, returning a raw JSON-RPC response.
    async fn respond_raw(&self, jrpc_req: JrpcRequest) -> JrpcResponse {
        if jrpc_req.jsonrpc != "2.0" {
            JrpcResponse {
                id: jrpc_req.id,
                jsonrpc: "2.0".into(),
                result: None,
                error: Some(JrpcError {
                    code: -32600,
                    message: "JSON-RPC version wrong".into(),
                    data: serde_json::Value::Null,
                }),
            }
        } else if let Some(response) = self.respond(&jrpc_req.method, jrpc_req.params).await {
            match response {
                Ok(response) => JrpcResponse {
                    id: jrpc_req.id,
                    jsonrpc: "2.0".into(),
                    result: Some(response),
                    error: None,
                },
                Err(err) => JrpcResponse {
                    id: jrpc_req.id,
                    jsonrpc: "2.0".into(),
                    result: None,
                    error: Some(JrpcError {
                        code: -1,
                        message: err.message,
                        data: err.details,
                    }),
                },
            }
        } else {
            JrpcResponse {
                id: jrpc_req.id,
                jsonrpc: "2.0".into(),
                result: None,
                error: Some(JrpcError {
                    code: -32601,
                    message: "Method not found".into(),
                    data: serde_json::Value::Null,
                }),
            }
        }
    }
}

/// A client-side nanorpc transport. The only method that needs to be implemented is [`RpcTransport::call_raw`], but clients typically call [`RpcTransport::call`].
///
/// # Example
///
/// ```ignore
/// use nanorpc::RpcTransport;
///
/// let transport: impl RpcTransport = connect_to_server().await;
/// let three: u32 = serde_json::from_value(transport.call("add", &[1.into(), 2.into()]).await
///         .expect("transport failed")
///         .expect("no such verb")
///         .expect("server error"))
///     .expect("JSON decoding error");
/// assert_eq!(three, 3);
/// ```
#[async_trait]
pub trait RpcTransport: Sync + Send + 'static {
    /// This error type represents *transport-level* errors, like communication errors and such.
    type Error: Sync + Send + 'static;

    /// Sends an RPC call to the remote side, returning the result. `Ok(None)` means that there is no transport-level error, but that the verb does not exist. This generally does not need a manual implementation.
    async fn call(
        &self,
        method: &str,
        params: &[serde_json::Value],
    ) -> Result<Option<Result<serde_json::Value, ServerError>>, Self::Error> {
        let reqid = format!("req-{}", fastrand::u64(..));
        let req = JrpcRequest {
            jsonrpc: "2.0".into(),
            id: JrpcId::String(reqid),
            method: method.into(),
            params: params
                .iter()
                .map(|s| serde_json::to_value(s).unwrap())
                .collect(),
        };
        let result = self.call_raw(req).await?;
        if let Some(res) = result.result {
            Ok(Some(Ok(res)))
        } else if let Some(res) = result.error {
            if res.code == -32600 {
                Ok(None)
            } else {
                Ok(Some(Err(ServerError {
                    code: res.code as u32,
                    message: res.message,
                    details: res.data,
                })))
            }
        } else {
            panic!("received malformed JrpcResponse from own call_raw")
        }
    }

    /// Sends an RPC call to the remote side, as a raw JSON-RPC request, receiving a raw JSON-RPC response.
    async fn call_raw(&self, req: JrpcRequest) -> Result<JrpcResponse, Self::Error>;
}

#[async_trait]
impl<T: RpcTransport + ?Sized> RpcTransport for Arc<T> {
    type Error = T::Error;

    async fn call_raw(&self, req: JrpcRequest) -> Result<JrpcResponse, Self::Error> {
        self.as_ref().call_raw(req).await
    }
}

#[async_trait]
impl<T: RpcTransport + ?Sized> RpcTransport for Box<T> {
    type Error = T::Error;

    async fn call_raw(&self, req: JrpcRequest) -> Result<JrpcResponse, Self::Error> {
        self.as_ref().call_raw(req).await
    }
}

// #[async_trait]
// impl<T: RpcService + Sync> RpcTransport for T {
//     type Error = Infallible;

//     async fn call_raw(&self, req: JrpcRequest) -> Result<JrpcResponse, Self::Error> {
//         Ok(self.respond_raw(req).await)
//     }
// }

#[cfg(test)]
mod tests {
    use crate::{self as nanorpc, ServerError};
    use nanorpc::{nanorpc_derive, RpcService};

    #[nanorpc_derive]
    #[async_trait::async_trait]
    pub trait MathProtocol {
        /// Adds two numbers
        async fn add(&self, x: f64, y: f64) -> f64;
        /// Multiplies two numbers
        async fn mult(&self, x: f64, y: f64) -> f64;
        /// Maybe fails
        async fn maybe_fail(&self) -> Result<f64, f64>;
    }

    struct Mather;

    #[async_trait::async_trait]
    impl MathProtocol for Mather {
        async fn add(&self, x: f64, y: f64) -> f64 {
            x + y
        }

        async fn mult(&self, x: f64, y: f64) -> f64 {
            x * y
        }

        async fn maybe_fail(&self) -> Result<f64, f64> {
            Err(12345.0)
        }
    }

    #[test]
    fn test_notfound_macro() {
        smol::future::block_on(async move {
            let service = MathService(Mather);
            assert_eq!(
                service
                    .respond("!nonexistent!", serde_json::from_str("[]").unwrap())
                    .await,
                None
            );
        });
    }

    #[test]
    fn test_simple_macro() {
        smol::future::block_on(async move {
            let service = MathService(Mather);
            assert_eq!(
                service
                    .respond("maybe_fail", serde_json::from_str("[]").unwrap())
                    .await
                    .unwrap()
                    .unwrap_err(),
                ServerError {
                    code: 1,
                    message: "12345".into(),
                    details: 12345.0f64.into()
                }
            );
            assert_eq!(
                service
                    .respond("add", serde_json::from_str("[1, 2]").unwrap())
                    .await
                    .unwrap()
                    .unwrap(),
                serde_json::Value::from(3.0f64)
            );
        });
    }
}