Skip to main content

rivetkit_client/
client.rs

1use std::{collections::HashMap, sync::Arc};
2
3use anyhow::Result;
4use serde_json::Value as JsonValue;
5
6use crate::{
7	common::{ActorKey, EncodingKind, TransportKind},
8	handle::ActorHandle,
9	protocol::query::*,
10	remote_manager::RemoteManager,
11};
12
13#[derive(Default)]
14pub struct GetWithIdOptions {
15	pub params: Option<JsonValue>,
16}
17
18#[derive(Default)]
19pub struct GetOptions {
20	pub params: Option<JsonValue>,
21}
22
23#[derive(Default)]
24pub struct GetOrCreateOptions {
25	pub params: Option<JsonValue>,
26	pub create_in_region: Option<String>,
27	pub create_with_input: Option<JsonValue>,
28	/// Overrides the client's configured pool name for this actor if it is created.
29	pub pool_name: Option<String>,
30}
31
32#[derive(Default)]
33pub struct CreateOptions {
34	pub params: Option<JsonValue>,
35	pub region: Option<String>,
36	pub input: Option<JsonValue>,
37	/// Overrides the client's configured pool name for this actor.
38	pub pool_name: Option<String>,
39}
40
41pub struct ClientConfig {
42	pub endpoint: String,
43	pub token: Option<String>,
44	pub namespace: Option<String>,
45	pub pool_name: Option<String>,
46	pub encoding: EncodingKind,
47	pub transport: TransportKind,
48	pub headers: Option<HashMap<String, String>>,
49	pub max_input_size: Option<usize>,
50	pub disable_metadata_lookup: bool,
51}
52
53impl ClientConfig {
54	pub fn new(endpoint: impl Into<String>) -> Self {
55		Self {
56			endpoint: endpoint.into(),
57			token: None,
58			namespace: None,
59			pool_name: None,
60			encoding: EncodingKind::Bare,
61			transport: TransportKind::WebSocket,
62			headers: None,
63			max_input_size: None,
64			disable_metadata_lookup: false,
65		}
66	}
67
68	pub fn token(mut self, token: impl Into<String>) -> Self {
69		self.token = Some(token.into());
70		self
71	}
72
73	pub fn token_opt(mut self, token: Option<String>) -> Self {
74		self.token = token;
75		self
76	}
77
78	pub fn namespace(mut self, namespace: impl Into<String>) -> Self {
79		self.namespace = Some(namespace.into());
80		self
81	}
82
83	pub fn pool_name(mut self, pool_name: impl Into<String>) -> Self {
84		self.pool_name = Some(pool_name.into());
85		self
86	}
87
88	pub fn encoding(mut self, encoding: EncodingKind) -> Self {
89		self.encoding = encoding;
90		self
91	}
92
93	pub fn transport(mut self, transport: TransportKind) -> Self {
94		self.transport = transport;
95		self
96	}
97
98	pub fn header(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
99		self.headers
100			.get_or_insert_with(HashMap::new)
101			.insert(key.into(), value.into());
102		self
103	}
104
105	pub fn headers(mut self, headers: HashMap<String, String>) -> Self {
106		self.headers = Some(headers);
107		self
108	}
109
110	pub fn max_input_size(mut self, max_input_size: usize) -> Self {
111		self.max_input_size = Some(max_input_size);
112		self
113	}
114
115	pub fn disable_metadata_lookup(mut self, disable: bool) -> Self {
116		self.disable_metadata_lookup = disable;
117		self
118	}
119}
120
121pub struct Client {
122	remote_manager: RemoteManager,
123	encoding_kind: EncodingKind,
124	transport_kind: TransportKind,
125	shutdown_tx: Arc<tokio::sync::broadcast::Sender<()>>,
126}
127
128impl Clone for Client {
129	fn clone(&self) -> Self {
130		Self {
131			remote_manager: self.remote_manager.clone(),
132			encoding_kind: self.encoding_kind,
133			transport_kind: self.transport_kind,
134			shutdown_tx: self.shutdown_tx.clone(),
135		}
136	}
137}
138
139impl std::fmt::Debug for Client {
140	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
141		f.debug_struct("Client")
142			.field("encoding_kind", &self.encoding_kind)
143			.field("transport_kind", &self.transport_kind)
144			.finish_non_exhaustive()
145	}
146}
147
148impl Client {
149	pub fn new(config: ClientConfig) -> Self {
150		let remote_manager = RemoteManager::from_config(
151			config.endpoint,
152			config.token,
153			config.namespace,
154			config.pool_name,
155			config.headers,
156			config.max_input_size,
157			config.disable_metadata_lookup,
158		);
159
160		Self {
161			remote_manager,
162			encoding_kind: config.encoding,
163			transport_kind: config.transport,
164			shutdown_tx: Arc::new(tokio::sync::broadcast::channel(1).0),
165		}
166	}
167
168	pub fn from_endpoint(endpoint: impl Into<String>) -> Self {
169		Self::new(ClientConfig::new(endpoint))
170	}
171
172	fn create_handle(&self, params: Option<JsonValue>, query: ActorQuery) -> ActorHandle {
173		let handle = ActorHandle::new(
174			self.remote_manager.clone(),
175			params,
176			query,
177			self.shutdown_tx.clone(),
178			self.transport_kind,
179			self.encoding_kind,
180		);
181
182		handle
183	}
184
185	pub fn get(&self, name: &str, key: ActorKey, opts: GetOptions) -> Result<ActorHandle> {
186		let actor_query = ActorQuery::GetForKey {
187			get_for_key: GetForKeyRequest {
188				name: name.to_string(),
189				key,
190			},
191		};
192
193		let handle = self.create_handle(opts.params, actor_query);
194
195		Ok(handle)
196	}
197
198	pub fn get_for_id(&self, name: &str, actor_id: &str, opts: GetOptions) -> Result<ActorHandle> {
199		let actor_query = ActorQuery::GetForId {
200			get_for_id: GetForIdRequest {
201				name: name.to_string(),
202				actor_id: actor_id.to_string(),
203			},
204		};
205
206		let handle = self.create_handle(opts.params, actor_query);
207
208		Ok(handle)
209	}
210
211	pub fn get_or_create(
212		&self,
213		name: &str,
214		key: ActorKey,
215		opts: GetOrCreateOptions,
216	) -> Result<ActorHandle> {
217		let input = opts.create_with_input;
218		let region = opts.create_in_region;
219
220		let actor_query = ActorQuery::GetOrCreateForKey {
221			get_or_create_for_key: GetOrCreateRequest {
222				name: name.to_string(),
223				key: key,
224				input,
225				region,
226				pool_name: opts.pool_name,
227			},
228		};
229
230		let handle = self.create_handle(opts.params, actor_query);
231
232		Ok(handle)
233	}
234
235	pub async fn create(
236		&self,
237		name: &str,
238		key: ActorKey,
239		opts: CreateOptions,
240	) -> Result<ActorHandle> {
241		let input = opts.input;
242		let _region = opts.region;
243
244		let actor_id = self
245			.remote_manager
246			.create_actor(name, &key, input, opts.pool_name)
247			.await?;
248
249		let get_query = ActorQuery::GetForId {
250			get_for_id: GetForIdRequest {
251				name: name.to_string(),
252				actor_id,
253			},
254		};
255
256		let handle = self.create_handle(opts.params, get_query);
257
258		Ok(handle)
259	}
260
261	pub fn disconnect(self) {
262		drop(self)
263	}
264
265	pub fn dispose(self) {
266		self.disconnect()
267	}
268}
269
270impl Drop for Client {
271	fn drop(&mut self) {
272		// Notify all subscribers to shutdown
273		let _ = self.shutdown_tx.send(());
274	}
275}