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).
56pub(crate) mod request;
7pub(crate) mod response;
89use std::ffi::NulError;
1011use serde::{Deserialize, Serialize};
1213use crate::util::Utf8CString;
1415/// 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.
31Number(i64),
32/// A string request ID.
33String(String),
34}
3536impl 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.)
40fn into_json_value(self) -> serde_json::Value {
41match self {
42 AnyRequestId::Number(n) => serde_json::Value::Number(n.into()),
43 AnyRequestId::String(s) => serde_json::Value::String(s),
44 }
45 }
46}
4748/// 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);
5859impl ObjectId {
60/// Return the global ID for an RPC connection.
61pub fn connection_id() -> Self {
62 ObjectId(
63"connection"
64.to_string()
65 .try_into()
66 .expect("Surprising NULs in string"),
67 )
68 }
6970/// Return this ID as a nul-terminated C string.
71#[cfg(feature = "ffi")]
72pub(crate) fn as_ptr(&self) -> *const std::ffi::c_char {
73self.0.as_ptr()
74 }
75}
7677impl TryFrom<String> for ObjectId {
78type Error = NulError;
7980fn try_from(value: String) -> Result<Self, Self::Error> {
81Ok(Self(Utf8CString::try_from(value)?))
82 }
83}
84impl AsRef<str> for ObjectId {
85fn as_ref(&self) -> &str {
86self.0.as_ref()
87 }
88}
89impl From<ObjectId> for String {
90fn from(v: ObjectId) -> String {
91 v.as_ref().into()
92 }
93}
9495/// 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 {
101fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
102where
103D: serde::Deserializer<'de>,
104 {
105/// Visitor to implement deserialize.
106struct Vis;
107impl<'de> serde::de::Visitor<'de> for Vis {
108type Value = JsonAnyObj;
109fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
110 formatter.write_str("a JSON object")
111 }
112fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
113where
114A: serde::de::MapAccess<'de>,
115 {
116// We need to iterate over the map, or else we'll get an error.
117while 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.
120let _: serde::de::IgnoredAny = k;
121let _: serde::de::IgnoredAny = v;
122 }
123Ok(JsonAnyObj {})
124 }
125 }
126127 deserializer.deserialize_map(Vis)
128 }
129}
130131#[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 @@ -->
146147use super::*;
148149#[test]
150fn any_obj_good() {
151for ok in [
152r#"{}"#,
153r#"{"7": 7}"#,
154r#"{"stuff": "nonsense", "this": {"that": "the other"}}"#,
155 ] {
156let _obj: JsonAnyObj = serde_json::from_str(ok).unwrap();
157 }
158 }
159#[test]
160fn any_obj_bad() {
161for bad in [r"[]", r#"7"#, r#"ksldjfa"#, r#""#, r#"{7:"foo"}"#] {
162let err: Result<JsonAnyObj, _> = serde_json::from_str(bad);
163assert!(err.is_err());
164 }
165 }
166}