Skip to main content

roboplc_rpc/
client.rs

1use core::{
2    marker::PhantomData,
3    mem,
4    sync::atomic::{AtomicU32, Ordering},
5};
6
7use serde::{Deserialize, Serialize};
8
9use crate::{dataformat, request::Request, response::Response, RpcError, RpcErrorKind, RpcResult};
10
11#[allow(clippy::module_name_repetitions)]
12#[derive(Default)]
13/// RPC client module, used to create RPC requests and handle RPC responses, call ids are `u32`
14pub struct RpcClient<'a, D, M, R> {
15    _phantom_d: PhantomData<D>,
16    _phantom_a: PhantomData<&'a ()>,
17    _phantom_m: PhantomData<M>,
18    _phantom_r: PhantomData<R>,
19    request_id: AtomicU32,
20}
21
22impl<'a, D, M, R> RpcClient<'a, D, M, R>
23where
24    D: dataformat::DataFormat,
25    M: Serialize + Deserialize<'a>,
26    R: Serialize + Deserialize<'a>,
27{
28    /// Create a new RPC client
29    pub fn new() -> Self {
30        Self {
31            _phantom_d: PhantomData,
32            _phantom_a: PhantomData,
33            _phantom_m: PhantomData,
34            _phantom_r: PhantomData,
35            request_id: AtomicU32::new(0),
36        }
37    }
38    /// Create a new RPC request
39    pub fn request(&self, method: M) -> Result<RpcClientRequest<D, M, R>, D::PackError> {
40        let id = self.request_id.fetch_add(1, Ordering::SeqCst);
41        let req = Request::new(id.into(), method);
42        let payload = D::pack(&req)?;
43        Ok(RpcClientRequest::new(Some(id), payload))
44    }
45    /// Create a new RPC request with no id (no response expected)
46    pub fn request0(&self, method: M) -> Result<RpcClientRequest<D, M, R>, D::PackError> {
47        let req = Request::new0(method);
48        let payload = D::pack(&req)?;
49        Ok(RpcClientRequest::new(None, payload))
50    }
51}
52
53/// RPC client request, no need to create directly if `RpcClient` is used
54pub struct RpcClientRequest<D, M, R> {
55    id: Option<u32>,
56    payload: Vec<u8>,
57    phantom_d: core::marker::PhantomData<D>,
58    phantom_m: core::marker::PhantomData<M>,
59    phantom_r: core::marker::PhantomData<R>,
60}
61
62impl<'a, D, M, R> RpcClientRequest<D, M, R>
63where
64    D: dataformat::DataFormat,
65    M: Serialize + Deserialize<'a>,
66    R: Serialize + Deserialize<'a>,
67{
68    /// Create a new RPC client request
69    pub fn new(id: Option<u32>, payload: Vec<u8>) -> Self {
70        Self {
71            id,
72            payload,
73            phantom_d: core::marker::PhantomData,
74            phantom_m: core::marker::PhantomData,
75            phantom_r: core::marker::PhantomData,
76        }
77    }
78    /// Get the request payload
79    pub fn payload(&self) -> &[u8] {
80        &self.payload
81    }
82    /// Take the request payload
83    pub fn take_payload(&mut self) -> Vec<u8> {
84        mem::take(&mut self.payload)
85    }
86    /// Handle the response payload
87    pub fn handle_response(&self, response_payload: &'a [u8]) -> RpcResult<R> {
88        let Some(id) = self.id else {
89            return Err(RpcError {
90                kind: RpcErrorKind::InvalidRequest,
91                message: Some("request ID is missing".to_owned()),
92            });
93        };
94        match D::unpack::<Response<R>>(response_payload) {
95            Ok(r) => {
96                let (res_id, res) = r.into_parts();
97                if res_id != id {
98                    return Err(RpcError {
99                        kind: RpcErrorKind::InvalidRequest,
100                        message: Some("response ID does not match request ID".to_owned()),
101                    });
102                }
103                res.into()
104            }
105            Err(e) => Err(RpcError {
106                kind: RpcErrorKind::ParseError,
107                message: Some(e.to_string()),
108            }),
109        }
110    }
111}