Skip to main content

roboplc_rpc/
server.rs

1use core::{fmt, marker::PhantomData};
2use tracing::error;
3
4use serde::{Deserialize, Serialize};
5
6use crate::{
7    dataformat::DataFormat,
8    request::Request,
9    response::{HandlerResponse, Response},
10    RpcError, RpcResult,
11};
12
13const ERR_FAILED_TO_PARSE: &str = "Failed to parse RPC request";
14
15/// JSON RPC server
16#[allow(clippy::module_name_repetitions)]
17pub struct RpcServer<'a, RPC: RpcServerHandler<'a>, M, SRC, R> {
18    _phantom_a: PhantomData<&'a ()>,
19    _phantom_m: PhantomData<M>,
20    _phantom_src: PhantomData<SRC>,
21    _phantom_r: PhantomData<R>,
22    rpc: RPC,
23}
24
25impl<'a, RPC: RpcServerHandler<'a, Method = M, Result = R, Source = SRC>, M, SRC, R>
26    RpcServer<'a, RPC, M, SRC, R>
27where
28    M: Deserialize<'a> + 'a,
29    R: Serialize + Deserialize<'a> + 'a,
30    SRC: fmt::Display,
31{
32    /// Create a new JSON RPC server
33    pub fn new(rpc: RPC) -> Self {
34        Self {
35            _phantom_a: PhantomData,
36            _phantom_m: PhantomData,
37            _phantom_src: PhantomData,
38            _phantom_r: PhantomData,
39            rpc,
40        }
41    }
42    /// Handle a JSON RPC request
43    pub fn handle_request(&'a self, request: Request<M>, source: SRC) -> Option<Response<R>> {
44        let result = match self.rpc.handle_call(request.method, source) {
45            Ok(v) => HandlerResponse::Ok(v),
46            Err(e) => HandlerResponse::Err(RpcError {
47                kind: e.kind,
48                message: e.message,
49            }),
50        };
51        request
52            .id
53            .map(move |id| Response::from_handler_response(id, result))
54    }
55    /// Handle a JSON RPC request from a payload
56    pub fn handle_request_payload<D>(&'a self, payload: &'a [u8], source: SRC) -> Option<Vec<u8>>
57    where
58        D: DataFormat,
59    {
60        macro_rules! serialize_response {
61            ($response:expr) => {{
62                match D::pack(&$response) {
63                    Ok(v) => Some(v),
64                    Err(error) => {
65                        error!(%error, "Failed to serialize response");
66                        if let Ok(response) = D::pack(
67                                &Response::<R>::from_server_error(
68                                    $response.id().clone(), error.to_string())) {
69                            Some(response)
70                        } else {
71                            None
72                        }
73                    }
74                }
75            }};
76        }
77        match D::unpack::<Request<M>>(payload) {
78            Ok(req) => self
79                .handle_request(req, source)
80                .and_then(|response| serialize_response!(response)),
81            Err(error) => {
82                error!(%source, %error, ERR_FAILED_TO_PARSE);
83                if let Ok(invalid) = D::unpack::<crate::request::InvalidRequest>(payload) {
84                    invalid
85                        .into_response(error.to_string())
86                        .and_then(|response: Response<R>| serialize_response!(response))
87                } else {
88                    None
89                }
90            }
91        }
92    }
93}
94
95/// RPC server trait
96#[allow(clippy::module_name_repetitions)]
97pub trait RpcServerHandler<'a> {
98    /// Methods to handle
99    type Method: Deserialize<'a>;
100    /// Result of the methods
101    type Result: Serialize + Deserialize<'a>;
102    /// Source of the call (IP address, etc.)
103    type Source;
104
105    /// A method to handle calls
106    fn handle_call(&'a self, method: Self::Method, source: Self::Source)
107        -> RpcResult<Self::Result>;
108}