Skip to main content

reifydb_client/
lib.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 ReifyDB
3#![cfg_attr(not(debug_assertions), deny(clippy::disallowed_methods))]
4#![cfg_attr(debug_assertions, warn(clippy::disallowed_methods))]
5#![allow(clippy::tabs_in_doc_comments)]
6
7/// Wire format for client-server communication.
8#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
9pub enum WireFormat {
10	#[default]
11	Json,
12	Proto,
13	Rbcf,
14}
15
16#[cfg(any(feature = "ws", feature = "grpc"))]
17mod changes;
18#[cfg(any(feature = "ws", feature = "grpc"))]
19pub mod client;
20#[cfg(all(feature = "dst", reifydb_single_threaded))]
21pub mod dst;
22#[cfg(feature = "grpc")]
23pub mod grpc;
24#[cfg(feature = "http")]
25pub mod http;
26#[cfg(any(feature = "http", feature = "ws"))]
27mod session;
28#[cfg(any(feature = "ws", feature = "grpc", all(feature = "dst", reifydb_single_threaded)))]
29pub mod subscription;
30#[cfg(feature = "ws")]
31mod utils;
32#[cfg(feature = "ws")]
33pub mod ws;
34
35// Re-export client types
36#[cfg(any(feature = "http", feature = "ws"))]
37use std::collections::HashMap;
38#[cfg(any(feature = "http", feature = "ws"))]
39use std::sync::Arc;
40
41#[cfg(all(feature = "dst", reifydb_single_threaded))]
42pub use dst::DstClient;
43#[cfg(feature = "grpc")]
44pub use grpc::{
45	BatchFramesEnvelope, BatchGrpcSubscription, BatchMemberHandle, BatchStreamEvent, GrpcChange, GrpcClient,
46	GrpcSubscription, RawChangePayload,
47};
48#[cfg(feature = "http")]
49pub use http::HttpClient;
50// Re-export derive macro
51pub use reifydb_client_derive::FromFrame;
52pub use reifydb_value as value;
53pub use reifydb_value::{
54	params::Params,
55	value::{
56		Value,
57		frame::{
58			column::FrameColumn,
59			data::FrameColumnData,
60			extract::FrameError,
61			frame::Frame,
62			from_frame::FromFrameError,
63			row::{FrameRow, FrameRows},
64		},
65		iso::{IsoDate, IsoDateTime, IsoDuration, IsoTime},
66		ordered_f32::OrderedF32,
67		ordered_f64::OrderedF64,
68		try_from::{FromValueError, TryFromValue, TryFromValueCoerce},
69		value_type::ValueType,
70	},
71};
72#[cfg(any(feature = "http", feature = "ws"))]
73use serde::{Deserialize, Serialize};
74use serde_json::Value as JsonValue;
75#[cfg(any(feature = "ws", feature = "grpc", all(feature = "dst", reifydb_single_threaded)))]
76pub use subscription::{BatchItem, HydrationConfig, SubscriptionConfig, build_subscription_rql};
77#[cfg(feature = "ws")]
78pub use ws::{WsBatchSubscription, WsClient};
79
80/// Server-reported metadata about a single executed request.
81#[cfg_attr(any(feature = "http", feature = "ws"), derive(Serialize, Deserialize))]
82#[derive(Debug, Clone)]
83pub struct ResponseMeta {
84	pub fingerprint: String,
85	pub duration: String,
86}
87
88/// Result type for admin operations
89#[derive(Debug)]
90pub struct AdminResult {
91	pub frames: Vec<Frame>,
92	pub meta: Option<ResponseMeta>,
93}
94
95/// Result type for command operations
96#[derive(Debug)]
97pub struct CommandResult {
98	pub frames: Vec<Frame>,
99	pub meta: Option<ResponseMeta>,
100}
101
102/// Result type for query operations
103#[derive(Debug)]
104pub struct QueryResult {
105	pub frames: Vec<Frame>,
106	pub meta: Option<ResponseMeta>,
107}
108
109/// Result type for authentication login operations
110#[derive(Debug, Clone)]
111pub struct LoginResult {
112	/// Session token for subsequent requests
113	pub token: String,
114	/// Identity UUID of the authenticated user
115	pub identity: String,
116}
117
118#[cfg(any(feature = "http", feature = "ws"))]
119/// Wire format for a single typed value: `{"type": "Int2", "value": "1234"}`.
120#[derive(Debug, Serialize, Deserialize)]
121pub struct WireValue {
122	#[serde(rename = "type")]
123	pub type_name: String,
124	pub value: String,
125}
126
127#[cfg(any(feature = "http", feature = "ws"))]
128/// Wire format for query parameters.
129///
130/// Either positional or named:
131/// - Positional: `[{"type":"Int2","value":"1234"}, ...]`
132/// - Named: `{"key": {"type":"Int2","value":"1234"}, ...}`
133#[derive(Debug, Serialize, Deserialize)]
134#[serde(untagged)]
135pub enum WireParams {
136	Positional(Vec<WireValue>),
137	Named(HashMap<String, WireValue>),
138}
139
140#[cfg(any(feature = "http", feature = "ws"))]
141fn value_to_wire(value: Value) -> WireValue {
142	let (type_name, value_str): (&str, String) = match &value {
143		Value::None {
144			..
145		} => ("None", "\u{27EA}none\u{27EB}".to_string()),
146		Value::Boolean(b) => ("Boolean", b.to_string()),
147		Value::Float4(f) => ("Float4", f.to_string()),
148		Value::Float8(f) => ("Float8", f.to_string()),
149		Value::Int1(i) => ("Int1", i.to_string()),
150		Value::Int2(i) => ("Int2", i.to_string()),
151		Value::Int4(i) => ("Int4", i.to_string()),
152		Value::Int8(i) => ("Int8", i.to_string()),
153		Value::Int16(i) => ("Int16", i.to_string()),
154		Value::Utf8(s) => ("Utf8", s.clone()),
155		Value::Uint1(u) => ("Uint1", u.to_string()),
156		Value::Uint2(u) => ("Uint2", u.to_string()),
157		Value::Uint4(u) => ("Uint4", u.to_string()),
158		Value::Uint8(u) => ("Uint8", u.to_string()),
159		Value::Uint16(u) => ("Uint16", u.to_string()),
160		Value::Uuid4(u) => ("Uuid4", u.to_string()),
161		Value::Uuid7(u) => ("Uuid7", u.to_string()),
162		Value::Date(d) => ("Date", d.to_string()),
163		Value::DateTime(dt) => ("DateTime", dt.to_string()),
164		Value::Time(t) => ("Time", t.to_string()),
165		Value::Duration(d) => ("Duration", d.to_iso_string()),
166		Value::Blob(b) => ("Blob", b.to_hex()),
167		Value::IdentityId(id) => ("IdentityId", id.to_string()),
168		Value::Int(i) => ("Int", i.to_string()),
169		Value::Uint(u) => ("Uint", u.to_string()),
170		Value::Decimal(d) => ("Decimal", d.to_string()),
171		Value::Any(v) => return value_to_wire(*v.clone()),
172		Value::DictionaryId(id) => ("DictionaryId", id.to_string()),
173		Value::Type(t) => ("ValueType", t.to_string()),
174		Value::List(items) => ("List", format!("{}", Value::List(items.clone()))),
175		Value::Record(fields) => ("Record", format!("{}", Value::Record(fields.clone()))),
176		Value::Tuple(items) => ("Tuple", format!("{}", Value::Tuple(items.clone()))),
177	};
178	WireValue {
179		type_name: type_name.to_string(),
180		value: value_str,
181	}
182}
183
184#[cfg(any(feature = "http", feature = "ws"))]
185pub fn params_to_wire(params: Params) -> Option<WireParams> {
186	match params {
187		Params::None => None,
188		Params::Positional(values) => Some(WireParams::Positional(
189			Arc::unwrap_or_clone(values).into_iter().map(value_to_wire).collect(),
190		)),
191		Params::Named(map) => Some(WireParams::Named(
192			Arc::unwrap_or_clone(map).into_iter().map(|(k, v)| (k, value_to_wire(v))).collect(),
193		)),
194	}
195}
196
197#[cfg(any(feature = "http", feature = "ws"))]
198#[derive(Debug, Serialize, Deserialize)]
199pub struct Request {
200	pub id: String,
201	#[serde(flatten)]
202	pub payload: RequestPayload,
203}
204
205#[cfg(any(feature = "http", feature = "ws"))]
206#[derive(Debug, Serialize, Deserialize)]
207#[serde(tag = "type", content = "payload")]
208pub enum RequestPayload {
209	Auth(AuthRequest),
210	Admin(AdminRequest),
211	Command(CommandRequest),
212	Query(QueryRequest),
213	Subscribe(SubscribeRequest),
214	Unsubscribe(UnsubscribeRequest),
215	BatchSubscribe(BatchSubscribeRequest),
216	BatchUnsubscribe(BatchUnsubscribeRequest),
217	Call(CallRequest),
218	Logout,
219}
220
221#[cfg(any(feature = "http", feature = "ws"))]
222#[derive(Debug, Serialize, Deserialize)]
223pub struct AdminRequest {
224	pub rql: String,
225	pub params: Option<WireParams>,
226	#[serde(skip_serializing_if = "Option::is_none")]
227	pub format: Option<String>,
228}
229
230#[cfg(any(feature = "http", feature = "ws"))]
231#[derive(Debug, Serialize, Deserialize)]
232pub struct AuthRequest {
233	#[serde(skip_serializing_if = "Option::is_none")]
234	pub token: Option<String>,
235	#[serde(skip_serializing_if = "Option::is_none")]
236	pub method: Option<String>,
237	#[serde(skip_serializing_if = "Option::is_none")]
238	pub credentials: Option<HashMap<String, String>>,
239}
240
241#[cfg(any(feature = "http", feature = "ws"))]
242#[derive(Debug, Serialize, Deserialize)]
243pub struct CommandRequest {
244	pub rql: String,
245	pub params: Option<WireParams>,
246	#[serde(skip_serializing_if = "Option::is_none")]
247	pub format: Option<String>,
248}
249
250#[cfg(any(feature = "http", feature = "ws"))]
251#[derive(Debug, Serialize, Deserialize)]
252pub struct QueryRequest {
253	pub rql: String,
254	pub params: Option<WireParams>,
255	#[serde(skip_serializing_if = "Option::is_none")]
256	pub format: Option<String>,
257}
258
259#[cfg(any(feature = "http", feature = "ws"))]
260#[derive(Debug, Serialize, Deserialize)]
261pub struct SubscribeRequest {
262	pub rql: String,
263	#[serde(skip_serializing_if = "Option::is_none")]
264	pub format: Option<String>,
265}
266
267#[cfg(any(feature = "http", feature = "ws"))]
268#[derive(Debug, Serialize, Deserialize)]
269pub struct UnsubscribeRequest {
270	pub subscription_id: String,
271}
272
273#[cfg(any(feature = "http", feature = "ws"))]
274#[derive(Debug, Serialize, Deserialize)]
275pub struct BatchSubscribeRequest {
276	pub queries: Vec<String>,
277	#[serde(skip_serializing_if = "Option::is_none")]
278	pub format: Option<String>,
279}
280
281#[cfg(any(feature = "http", feature = "ws"))]
282#[derive(Debug, Serialize, Deserialize)]
283pub struct BatchUnsubscribeRequest {
284	pub batch_id: String,
285}
286
287#[cfg(any(feature = "http", feature = "ws"))]
288#[derive(Debug, Serialize, Deserialize)]
289pub struct CallRequest {
290	pub name: String,
291	pub params: Option<WireParams>,
292}
293
294#[cfg(any(feature = "http", feature = "ws"))]
295#[derive(Debug, Serialize, Deserialize)]
296pub struct Response {
297	pub id: String,
298	#[serde(flatten)]
299	pub payload: ResponsePayload,
300}
301
302#[cfg(any(feature = "http", feature = "ws"))]
303#[derive(Debug, Serialize, Deserialize)]
304#[serde(tag = "type", content = "payload")]
305pub enum ResponsePayload {
306	Auth(AuthResponse),
307	Err(ErrResponse),
308	Admin(AdminResponse),
309	Command(CommandResponse),
310	Query(QueryResponse),
311	Subscribed(SubscribedResponse),
312	Unsubscribed(UnsubscribedResponse),
313	BatchSubscribed(BatchSubscribedResponse),
314	BatchUnsubscribed(BatchUnsubscribedResponse),
315	Call(CallResponse),
316	Logout(LogoutResponsePayload),
317}
318
319#[cfg(any(feature = "http", feature = "ws"))]
320#[derive(Debug, Serialize, Deserialize)]
321pub struct AdminResponse {
322	pub content_type: String,
323	pub body: JsonValue,
324	#[serde(default)]
325	pub meta: Option<ResponseMeta>,
326}
327
328#[cfg(any(feature = "http", feature = "ws"))]
329use reifydb_value::error::Diagnostic;
330
331#[cfg(any(feature = "http", feature = "ws"))]
332#[derive(Debug, Serialize, Deserialize)]
333pub struct AuthResponse {
334	#[serde(skip_serializing_if = "Option::is_none")]
335	pub status: Option<String>,
336	#[serde(skip_serializing_if = "Option::is_none")]
337	pub token: Option<String>,
338	#[serde(skip_serializing_if = "Option::is_none")]
339	pub identity: Option<String>,
340}
341
342#[cfg(any(feature = "http", feature = "ws"))]
343#[derive(Debug, Serialize, Deserialize)]
344pub struct ErrResponse {
345	pub diagnostic: Diagnostic,
346}
347
348#[cfg(any(feature = "http", feature = "ws"))]
349#[derive(Debug, Serialize, Deserialize)]
350pub struct CommandResponse {
351	pub content_type: String,
352	pub body: JsonValue,
353	#[serde(default)]
354	pub meta: Option<ResponseMeta>,
355}
356
357#[cfg(any(feature = "http", feature = "ws"))]
358#[derive(Debug, Serialize, Deserialize)]
359pub struct QueryResponse {
360	pub content_type: String,
361	pub body: JsonValue,
362	#[serde(default)]
363	pub meta: Option<ResponseMeta>,
364}
365
366#[cfg(any(feature = "http", feature = "ws"))]
367#[derive(Debug, Serialize, Deserialize)]
368pub struct CallResponse {
369	pub content_type: String,
370	pub body: JsonValue,
371	#[serde(default)]
372	pub meta: Option<ResponseMeta>,
373}
374
375#[cfg(any(feature = "http", feature = "ws"))]
376#[derive(Debug, Serialize, Deserialize)]
377pub struct SubscribedResponse {
378	pub subscription_id: String,
379}
380
381#[cfg(any(feature = "http", feature = "ws"))]
382#[derive(Debug, Serialize, Deserialize)]
383pub struct UnsubscribedResponse {
384	pub subscription_id: String,
385}
386
387#[cfg(any(feature = "http", feature = "ws"))]
388#[derive(Debug, Serialize, Deserialize)]
389pub struct BatchSubscribedResponse {
390	pub batch_id: String,
391	pub members: Vec<BatchMemberInfo>,
392}
393
394#[cfg_attr(any(feature = "http", feature = "ws"), derive(Serialize, Deserialize))]
395#[derive(Debug, Clone)]
396pub struct BatchMemberInfo {
397	pub index: usize,
398	pub subscription_id: String,
399}
400
401#[cfg(any(feature = "http", feature = "ws"))]
402#[derive(Debug, Serialize, Deserialize)]
403pub struct BatchUnsubscribedResponse {
404	pub batch_id: String,
405}
406
407#[cfg(any(feature = "http", feature = "ws"))]
408#[derive(Debug, Serialize, Deserialize)]
409pub struct LogoutResponsePayload {
410	pub status: String,
411}
412
413#[cfg(any(feature = "http", feature = "ws"))]
414#[derive(Debug, Serialize, Deserialize)]
415#[serde(tag = "type", content = "payload")]
416pub enum ServerPush {
417	Change(WireChangePayload),
418	BatchChange(WireBatchChangePayload),
419	BatchMemberClosed(BatchMemberClosedPayload),
420	BatchClosed(BatchClosedPayload),
421}
422
423#[cfg(any(feature = "http", feature = "ws"))]
424#[derive(Debug, Clone, Serialize, Deserialize)]
425pub struct WireChangePayload {
426	pub subscription_id: String,
427	pub content_type: String,
428	pub body: JsonValue,
429}
430
431#[cfg(any(feature = "http", feature = "ws"))]
432#[derive(Debug, Clone, Serialize, Deserialize)]
433pub struct WireBatchChangePayload {
434	pub batch_id: String,
435	pub entries: Vec<WireBatchChangeEntry>,
436}
437
438#[cfg(any(feature = "http", feature = "ws"))]
439#[derive(Debug, Clone, Serialize, Deserialize)]
440pub struct WireBatchChangeEntry {
441	pub subscription_id: String,
442	pub content_type: String,
443	pub body: JsonValue,
444}
445
446#[cfg_attr(any(feature = "http", feature = "ws"), derive(Serialize, Deserialize))]
447#[derive(Debug, Clone, Copy, PartialEq, Eq)]
448pub enum ChangeKind {
449	Insert,
450	Update,
451	Remove,
452}
453
454#[cfg_attr(any(feature = "http", feature = "ws"), derive(Serialize, Deserialize))]
455#[derive(Debug, Clone)]
456pub struct ChangePayload {
457	pub subscription_id: String,
458	pub kind: ChangeKind,
459	pub content_type: String,
460	pub body: JsonValue,
461	#[cfg_attr(any(feature = "http", feature = "ws"), serde(skip, default))]
462	pub frames: Option<Vec<Frame>>,
463}
464
465#[cfg_attr(any(feature = "http", feature = "ws"), derive(Serialize, Deserialize))]
466#[derive(Debug, Clone)]
467pub struct BatchChangePayload {
468	pub batch_id: String,
469	pub entries: Vec<BatchChangeEntry>,
470}
471
472#[cfg_attr(any(feature = "http", feature = "ws"), derive(Serialize, Deserialize))]
473#[derive(Debug, Clone)]
474pub struct BatchChangeEntry {
475	pub subscription_id: String,
476	pub kind: ChangeKind,
477	pub content_type: String,
478	pub body: JsonValue,
479	#[cfg_attr(any(feature = "http", feature = "ws"), serde(skip, default))]
480	pub frames: Option<Vec<Frame>>,
481	#[cfg_attr(any(feature = "http", feature = "ws"), serde(skip, default))]
482	pub decode_error: Option<String>,
483}
484
485#[cfg_attr(any(feature = "http", feature = "ws"), derive(Serialize, Deserialize))]
486#[derive(Debug, Clone)]
487pub struct BatchMemberClosedPayload {
488	pub batch_id: String,
489	pub subscription_id: String,
490}
491
492#[cfg_attr(any(feature = "http", feature = "ws"), derive(Serialize, Deserialize))]
493#[derive(Debug, Clone)]
494pub struct BatchClosedPayload {
495	pub batch_id: String,
496}
497
498#[derive(Debug, Clone)]
499pub enum BatchPushEvent {
500	Change(BatchChangePayload),
501	MemberClosed(BatchMemberClosedPayload),
502	Closed(BatchClosedPayload),
503}