arti_rpc_client_core/
msgs.rs

1//! Support for decoding and encoding RPC messages.
2//!
3//! Every message is either a Request (sent to Arti)
4//! or a Response (received from Arti).
5
6pub(crate) mod request;
7pub(crate) mod response;
8
9use std::ffi::NulError;
10
11use serde::{Deserialize, Serialize};
12
13use crate::util::Utf8CString;
14
15/// An identifier for a request made to the Arti RPC system.
16///
17/// Every request must have an ID, chosen by the application that's sending it.
18/// If these IDs are not distinct, the application can get confused about
19/// which reply corresponds to which request.
20///
21/// The [`RpcConn`](crate::conn::RpcConn) type can generate unique IDs
22/// for outbound requests as needed.
23#[derive(Serialize, Deserialize, Debug, Clone, Hash, Eq, PartialEq, derive_more::From)]
24#[serde(untagged)]
25#[non_exhaustive]
26pub enum AnyRequestId {
27    /// A numeric request ID.
28    ///
29    /// Note that values larger than `±2^53-1` may not work with all
30    /// JSON implementations.
31    Number(i64),
32    /// A string request ID.
33    String(String),
34}
35
36impl AnyRequestId {
37    /// Convert this request ID into a json value.
38    //
39    // (This is a private function because we don't want to expose serde_json in our API.)
40    fn into_json_value(self) -> serde_json::Value {
41        match self {
42            AnyRequestId::Number(n) => serde_json::Value::Number(n.into()),
43            AnyRequestId::String(s) => serde_json::Value::String(s),
44        }
45    }
46}
47
48/// An identifier for some object visible to the Arti RPC system.
49///
50/// A single object may have multiple underlying identifiers.
51/// These identifiers should always be treated as opaque
52/// from the application's perspective.
53#[derive(
54    Serialize, Deserialize, Debug, Clone, Hash, Eq, PartialEq, derive_more::From, derive_more::Into,
55)]
56#[serde(transparent)]
57pub struct ObjectId(Utf8CString);
58
59impl ObjectId {
60    /// Return the global ID for an RPC connection.
61    pub fn connection_id() -> Self {
62        ObjectId(
63            "connection"
64                .to_string()
65                .try_into()
66                .expect("Surprising NULs in string"),
67        )
68    }
69
70    /// Return this ID as a nul-terminated C string.
71    #[cfg(feature = "ffi")]
72    pub(crate) fn as_ptr(&self) -> *const std::ffi::c_char {
73        self.0.as_ptr()
74    }
75}
76
77impl TryFrom<String> for ObjectId {
78    type Error = NulError;
79
80    fn try_from(value: String) -> Result<Self, Self::Error> {
81        Ok(Self(Utf8CString::try_from(value)?))
82    }
83}
84impl AsRef<str> for ObjectId {
85    fn as_ref(&self) -> &str {
86        self.0.as_ref()
87    }
88}
89impl From<ObjectId> for String {
90    fn from(v: ObjectId) -> String {
91        v.as_ref().into()
92    }
93}
94
95/// Serde helper: deserializes (and discards) the contents of any json Object,
96/// and does not accept any other type.
97#[derive(Debug)]
98struct JsonAnyObj {}
99// Note: We can't just use `derive(Deserialize)` here, since that would permit empty arrays.
100impl<'de> serde::de::Deserialize<'de> for JsonAnyObj {
101    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
102    where
103        D: serde::Deserializer<'de>,
104    {
105        /// Visitor to implement deserialize.
106        struct Vis;
107        impl<'de> serde::de::Visitor<'de> for Vis {
108            type Value = JsonAnyObj;
109            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
110                formatter.write_str("a JSON object")
111            }
112            fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
113            where
114                A: serde::de::MapAccess<'de>,
115            {
116                // We need to iterate over the map, or else we'll get an error.
117                while let Some((k, v)) = map.next_entry()? {
118                    // It's okay to allow any type here for keys;
119                    // serde_json won't deserialize a key  unless it is a string.
120                    let _: serde::de::IgnoredAny = k;
121                    let _: serde::de::IgnoredAny = v;
122                }
123                Ok(JsonAnyObj {})
124            }
125        }
126
127        deserializer.deserialize_map(Vis)
128    }
129}
130
131#[cfg(test)]
132mod test {
133    // @@ begin test lint list maintained by maint/add_warning @@
134    #![allow(clippy::bool_assert_comparison)]
135    #![allow(clippy::clone_on_copy)]
136    #![allow(clippy::dbg_macro)]
137    #![allow(clippy::mixed_attributes_style)]
138    #![allow(clippy::print_stderr)]
139    #![allow(clippy::print_stdout)]
140    #![allow(clippy::single_char_pattern)]
141    #![allow(clippy::unwrap_used)]
142    #![allow(clippy::unchecked_duration_subtraction)]
143    #![allow(clippy::useless_vec)]
144    #![allow(clippy::needless_pass_by_value)]
145    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
146
147    use super::*;
148
149    #[test]
150    fn any_obj_good() {
151        for ok in [
152            r#"{}"#,
153            r#"{"7": 7}"#,
154            r#"{"stuff": "nonsense", "this": {"that": "the other"}}"#,
155        ] {
156            let _obj: JsonAnyObj = serde_json::from_str(ok).unwrap();
157        }
158    }
159    #[test]
160    fn any_obj_bad() {
161        for bad in [r"[]", r#"7"#, r#"ksldjfa"#, r#""#, r#"{7:"foo"}"#] {
162            let err: Result<JsonAnyObj, _> = serde_json::from_str(bad);
163            assert!(err.is_err());
164        }
165    }
166}