roboplc_rpc/
lib.rs

1#![ doc = include_str!( concat!( env!( "CARGO_MANIFEST_DIR" ), "/", "README.md" ) ) ]
2#![cfg_attr(not(feature = "std"), no_std)]
3#![deny(missing_docs)]
4
5use serde::{Deserialize, Serialize};
6
7const JSONRPC_VERSION: &str = "2.0";
8
9#[cfg(feature = "canonical")]
10const VERSION_HEADER: Option<()> = Some(());
11#[cfg(not(feature = "canonical"))]
12const VERSION_HEADER: Option<()> = None;
13
14#[cfg(feature = "canonical")]
15const ERR_INVALID_PROTOCOL_VERSION: &str = "Invalid protocol version";
16
17#[cfg(feature = "std")]
18/// RPC call id (`u32` in `no_std` mode, `serde_json::Value` in `std` mode)
19pub type Id = serde_json::Value;
20#[cfg(not(feature = "std"))]
21/// RPC call id (`u32` in `no_std` mode, `serde_json::Value` in `std` mode)
22pub type Id = u32;
23
24#[cfg(feature = "std")]
25type String = std::string::String;
26#[cfg(not(feature = "std"))]
27type String = heapless::String<128>;
28
29#[cfg(feature = "std")]
30/// RPC client
31pub mod client;
32#[cfg(feature = "std")]
33/// Data serialization formats
34pub mod dataformat;
35/// RPC request
36pub mod request;
37/// RPC response
38pub mod response;
39#[cfg(feature = "std")]
40/// RPC server
41pub mod server;
42/// Miscellaneous tools
43pub mod tools;
44
45fn de_validate_version<'de, D>(deserializer: D) -> Result<Option<()>, D::Error>
46where
47    D: serde::Deserializer<'de>,
48{
49    let version: Option<&str> = Deserialize::deserialize(deserializer)?;
50    #[cfg(feature = "canonical")]
51    if version.map_or(false, |v| v != JSONRPC_VERSION) {
52        return Err(serde::de::Error::custom(ERR_INVALID_PROTOCOL_VERSION));
53    }
54    Ok(version.map(|_| ()))
55}
56
57#[allow(clippy::trivially_copy_pass_by_ref)]
58fn serialize_version<S>(_: &Option<()>, serializer: S) -> Result<S::Ok, S::Error>
59where
60    S: serde::Serializer,
61{
62    JSONRPC_VERSION.serialize(serializer)
63}
64
65const RPC_ERROR_PARSE_ERROR: i16 = -32700;
66const RPC_ERROR_INVALID_REQUEST: i16 = -32600;
67const RPC_ERROR_METHOD_NOT_FOUND: i16 = -32601;
68const RPC_ERROR_INVALID_PARAMS: i16 = -32602;
69const RPC_ERROR_INTERNAL_ERROR: i16 = -32603;
70
71/// RPC error kind
72#[derive(Debug, Clone, Copy, PartialEq, Eq)]
73pub enum RpcErrorKind {
74    /// Parse error
75    ParseError,
76    /// Invalid request
77    InvalidRequest,
78    /// Method not found
79    MethodNotFound,
80    /// Invalid parameters (reserved for the future/manual use)
81    InvalidParams,
82    /// Internal error
83    InternalError,
84    /// Custom error
85    Custom(i16),
86}
87
88#[cfg(feature = "std")]
89impl core::fmt::Display for RpcErrorKind {
90    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
91        write!(f, "{}", i16::from(*self))
92    }
93}
94
95impl From<i16> for RpcErrorKind {
96    fn from(code: i16) -> Self {
97        match code {
98            RPC_ERROR_PARSE_ERROR => RpcErrorKind::ParseError,
99            RPC_ERROR_INVALID_REQUEST => RpcErrorKind::InvalidRequest,
100            RPC_ERROR_METHOD_NOT_FOUND => RpcErrorKind::MethodNotFound,
101            RPC_ERROR_INVALID_PARAMS => RpcErrorKind::InvalidParams,
102            RPC_ERROR_INTERNAL_ERROR => RpcErrorKind::InternalError,
103            _ => RpcErrorKind::Custom(code),
104        }
105    }
106}
107
108impl From<RpcErrorKind> for i16 {
109    fn from(code: RpcErrorKind) -> Self {
110        match code {
111            RpcErrorKind::ParseError => RPC_ERROR_PARSE_ERROR,
112            RpcErrorKind::InvalidRequest => RPC_ERROR_INVALID_REQUEST,
113            RpcErrorKind::MethodNotFound => RPC_ERROR_METHOD_NOT_FOUND,
114            RpcErrorKind::InvalidParams => RPC_ERROR_INVALID_PARAMS,
115            RpcErrorKind::InternalError => RPC_ERROR_INTERNAL_ERROR,
116            RpcErrorKind::Custom(code) => code,
117        }
118    }
119}
120
121impl Serialize for RpcErrorKind {
122    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
123    where
124        S: serde::Serializer,
125    {
126        i16::from(*self).serialize(serializer)
127    }
128}
129
130impl<'de> Deserialize<'de> for RpcErrorKind {
131    fn deserialize<D>(deserializer: D) -> Result<RpcErrorKind, D::Error>
132    where
133        D: serde::Deserializer<'de>,
134    {
135        i16::deserialize(deserializer).map(RpcErrorKind::from)
136    }
137}
138
139/// RPC error type
140#[derive(Serialize, Deserialize, Debug)]
141pub struct RpcError {
142    #[serde(rename = "code")]
143    kind: RpcErrorKind,
144    #[serde(skip_serializing_if = "Option::is_none")]
145    message: Option<String>,
146}
147
148impl RpcError {
149    /// Create a new error
150    pub fn new0(kind: RpcErrorKind) -> Self {
151        Self {
152            kind,
153            message: None,
154        }
155    }
156    /// Create a new error with a message. The message must be `String` to have compatibility with
157    /// `no_std` mode.
158    pub fn new(kind: RpcErrorKind, message: String) -> Self {
159        Self {
160            kind,
161            message: Some(message),
162        }
163    }
164    /// Get the error kind
165    pub fn kind(&self) -> RpcErrorKind {
166        self.kind
167    }
168    /// Get the error message
169    pub fn message(&self) -> Option<&str> {
170        self.message.as_deref()
171    }
172}
173
174#[cfg(feature = "std")]
175impl core::fmt::Display for RpcError {
176    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
177        if let Some(message) = &self.message {
178            write!(f, "{} ({})", message, self.kind)
179        } else {
180            write!(f, "{}", self.kind)
181        }
182    }
183}
184
185#[cfg(feature = "std")]
186impl std::error::Error for RpcError {}
187
188/// RPC result type alias for RPC handler
189pub type RpcResult<R> = Result<R, RpcError>;