Skip to main content

rivetkit_client/
common.rs

1#[allow(dead_code)]
2pub const VERSION: &str = env!("CARGO_PKG_VERSION");
3pub const USER_AGENT_VALUE: &str = concat!("ActorClient-Rust/", env!("CARGO_PKG_VERSION"));
4
5// Headers
6#[allow(dead_code)]
7pub const HEADER_ACTOR_QUERY: &str = "x-rivet-query";
8pub const HEADER_ENCODING: &str = "x-rivet-encoding";
9pub const HEADER_CONN_PARAMS: &str = "x-rivet-conn-params";
10#[allow(dead_code)]
11pub const HEADER_ACTOR_ID: &str = "x-rivet-actor";
12#[allow(dead_code)]
13pub const HEADER_CONN_ID: &str = "x-rivet-conn";
14#[allow(dead_code)]
15pub const HEADER_CONN_TOKEN: &str = "x-rivet-conn-token";
16
17// Gateway headers
18pub const HEADER_RIVET_TARGET: &str = "x-rivet-target";
19pub const HEADER_RIVET_ACTOR: &str = "x-rivet-actor";
20pub const HEADER_RIVET_TOKEN: &str = "x-rivet-token";
21pub const HEADER_RIVET_NAMESPACE: &str = "x-rivet-namespace";
22
23// Paths
24pub const PATH_CONNECT_WEBSOCKET: &str = "/connect";
25pub const PATH_WEBSOCKET_PREFIX: &str = "/websocket/";
26
27pub type RawWebSocket =
28	tokio_tungstenite::WebSocketStream<tokio_tungstenite::MaybeTlsStream<tokio::net::TcpStream>>;
29
30// WebSocket protocol prefixes
31pub const WS_PROTOCOL_STANDARD: &str = "rivet";
32pub const WS_PROTOCOL_TARGET: &str = "rivet_target.";
33pub const WS_PROTOCOL_ACTOR: &str = "rivet_actor.";
34pub const WS_PROTOCOL_ENCODING: &str = "rivet_encoding.";
35pub const WS_PROTOCOL_CONN_PARAMS: &str = "rivet_conn_params.";
36pub const WS_PROTOCOL_CONN_ID: &str = "rivet_conn.";
37pub const WS_PROTOCOL_CONN_TOKEN: &str = "rivet_conn_token.";
38pub const WS_PROTOCOL_TOKEN: &str = "rivet_token.";
39
40#[derive(Debug, Clone, Copy)]
41pub enum TransportKind {
42	WebSocket,
43	Sse,
44}
45
46#[derive(Debug, Clone, Copy, PartialEq, Eq)]
47pub enum EncodingKind {
48	Json,
49	Cbor,
50	Bare,
51}
52
53impl EncodingKind {
54	pub fn as_str(&self) -> &str {
55		match self {
56			EncodingKind::Json => "json",
57			EncodingKind::Cbor => "cbor",
58			EncodingKind::Bare => "bare",
59		}
60	}
61}
62
63impl Default for EncodingKind {
64	fn default() -> Self {
65		Self::Bare
66	}
67}
68
69impl ToString for EncodingKind {
70	fn to_string(&self) -> String {
71		self.as_str().to_string()
72	}
73}
74
75// Max size of each entry is 128 bytes
76pub type ActorKey = Vec<String>;
77
78/// Serialize an actor key for the engine HTTP API in the canonical
79/// slash-escaped format shared by the TS SDK (`serializeActorKey` in
80/// `rivetkit/src/actor/keys.ts`) and the runner-side parser
81/// (`rivetkit-core/src/registry/envoy_callbacks.rs`):
82///
83/// - empty key array -> the marker `"/"`
84/// - empty segment -> the marker backslash-`0`
85/// - backslash and `/` inside a segment are escaped with a leading backslash
86/// - segments joined with `/`
87///
88/// Previously this crate sent `serde_json::to_string(key)` (e.g.
89/// `["my-key"]`), which the runner-side parser treated as ONE opaque
90/// segment, so `ctx.key()` inside the actor returned
91/// `[String("[\"my-key\"]")]` instead of `[String("my-key")]`.
92pub fn serialize_actor_key(key: &ActorKey) -> String {
93	const KEY_SEPARATOR: char = '/';
94	const EMPTY_KEY: &str = "/";
95
96	if key.is_empty() {
97		return EMPTY_KEY.to_string();
98	}
99
100	key.iter()
101		.map(|part| {
102			if part.is_empty() {
103				return "\\0".to_string();
104			}
105			let mut escaped = String::with_capacity(part.len());
106			for ch in part.chars() {
107				if ch == '\\' || ch == KEY_SEPARATOR {
108					escaped.push('\\');
109				}
110				escaped.push(ch);
111			}
112			escaped
113		})
114		.collect::<Vec<_>>()
115		.join(&KEY_SEPARATOR.to_string())
116}
117
118#[cfg(test)]
119mod actor_key_tests {
120	use super::serialize_actor_key;
121
122	#[test]
123	fn matches_the_ts_sdk_format() {
124		assert_eq!(serialize_actor_key(&vec![]), "/");
125		assert_eq!(serialize_actor_key(&vec!["a".into()]), "a");
126		assert_eq!(serialize_actor_key(&vec!["a".into(), "b".into()]), "a/b");
127		assert_eq!(serialize_actor_key(&vec!["".into()]), "\\0");
128		assert_eq!(serialize_actor_key(&vec!["a/b".into()]), "a\\/b");
129		assert_eq!(serialize_actor_key(&vec!["a\\b".into()]), "a\\\\b");
130	}
131}