Skip to main content

reifydb_client/
lib.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2025 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#[cfg(all(feature = "dst", reifydb_single_threaded))]
7pub mod dst;
8#[cfg(feature = "grpc")]
9pub mod grpc;
10#[cfg(feature = "http")]
11pub mod http;
12#[cfg(any(feature = "http", feature = "ws"))]
13mod session;
14#[cfg(feature = "ws")]
15mod utils;
16#[cfg(feature = "ws")]
17pub mod ws;
18
19// Re-export client types
20#[cfg(any(feature = "http", feature = "ws"))]
21use std::collections::HashMap;
22#[cfg(any(feature = "http", feature = "ws"))]
23use std::sync::Arc;
24
25#[cfg(all(feature = "dst", reifydb_single_threaded))]
26pub use dst::DstClient;
27#[cfg(feature = "grpc")]
28pub use grpc::{GrpcClient, GrpcSubscription};
29#[cfg(feature = "http")]
30pub use http::HttpClient;
31// Re-export derive macro
32pub use reifydb_client_derive::FromFrame;
33// Re-export commonly used types from reifydb-type
34pub use reifydb_type as r#type;
35pub use reifydb_type::{
36	params::Params,
37	value::{
38		Value,
39		frame::{
40			column::FrameColumn,
41			data::FrameColumnData,
42			extract::FrameError,
43			frame::Frame,
44			from_frame::FromFrameError,
45			row::{FrameRow, FrameRows},
46		},
47		ordered_f32::OrderedF32,
48		ordered_f64::OrderedF64,
49		try_from::{FromValueError, TryFromValue, TryFromValueCoerce},
50		r#type::Type,
51	},
52};
53#[cfg(any(feature = "http", feature = "ws"))]
54use serde::{Deserialize, Serialize};
55#[cfg(any(feature = "http", feature = "ws"))]
56use serde_json::Value as JsonValue;
57#[cfg(feature = "ws")]
58pub use ws::WsClient;
59
60/// Result type for admin operations
61#[derive(Debug)]
62pub struct AdminResult {
63	pub frames: Vec<Frame>,
64}
65
66/// Result type for command operations
67#[derive(Debug)]
68pub struct CommandResult {
69	pub frames: Vec<Frame>,
70}
71
72/// Result type for query operations
73#[derive(Debug)]
74pub struct QueryResult {
75	pub frames: Vec<Frame>,
76}
77
78/// Result type for authentication login operations
79#[derive(Debug, Clone)]
80pub struct LoginResult {
81	/// Session token for subsequent requests
82	pub token: String,
83	/// Identity UUID of the authenticated user
84	pub identity: String,
85}
86
87#[cfg(any(feature = "http", feature = "ws"))]
88/// Wire format for a single typed value: `{"type": "Int2", "value": "1234"}`.
89#[derive(Debug, Serialize, Deserialize)]
90pub struct WireValue {
91	#[serde(rename = "type")]
92	pub type_name: String,
93	pub value: String,
94}
95
96#[cfg(any(feature = "http", feature = "ws"))]
97/// Wire format for query parameters.
98///
99/// Either positional or named:
100/// - Positional: `[{"type":"Int2","value":"1234"}, ...]`
101/// - Named: `{"key": {"type":"Int2","value":"1234"}, ...}`
102#[derive(Debug, Serialize, Deserialize)]
103#[serde(untagged)]
104pub enum WireParams {
105	Positional(Vec<WireValue>),
106	Named(HashMap<String, WireValue>),
107}
108
109#[cfg(any(feature = "http", feature = "ws"))]
110fn value_to_wire(value: Value) -> WireValue {
111	let (type_name, value_str): (&str, String) = match &value {
112		Value::None {
113			..
114		} => ("None", "\u{27EA}none\u{27EB}".to_string()),
115		Value::Boolean(b) => ("Boolean", b.to_string()),
116		Value::Float4(f) => ("Float4", f.to_string()),
117		Value::Float8(f) => ("Float8", f.to_string()),
118		Value::Int1(i) => ("Int1", i.to_string()),
119		Value::Int2(i) => ("Int2", i.to_string()),
120		Value::Int4(i) => ("Int4", i.to_string()),
121		Value::Int8(i) => ("Int8", i.to_string()),
122		Value::Int16(i) => ("Int16", i.to_string()),
123		Value::Utf8(s) => ("Utf8", s.clone()),
124		Value::Uint1(u) => ("Uint1", u.to_string()),
125		Value::Uint2(u) => ("Uint2", u.to_string()),
126		Value::Uint4(u) => ("Uint4", u.to_string()),
127		Value::Uint8(u) => ("Uint8", u.to_string()),
128		Value::Uint16(u) => ("Uint16", u.to_string()),
129		Value::Uuid4(u) => ("Uuid4", u.to_string()),
130		Value::Uuid7(u) => ("Uuid7", u.to_string()),
131		Value::Date(d) => ("Date", d.to_string()),
132		Value::DateTime(dt) => ("DateTime", dt.to_string()),
133		Value::Time(t) => ("Time", t.to_string()),
134		Value::Duration(d) => ("Duration", d.to_iso_string()),
135		Value::Blob(b) => ("Blob", b.to_hex()),
136		Value::IdentityId(id) => ("IdentityId", id.to_string()),
137		Value::Int(i) => ("Int", i.to_string()),
138		Value::Uint(u) => ("Uint", u.to_string()),
139		Value::Decimal(d) => ("Decimal", d.to_string()),
140		Value::Any(v) => return value_to_wire(*v.clone()),
141		Value::DictionaryId(id) => ("DictionaryId", id.to_string()),
142		Value::Type(t) => ("Type", t.to_string()),
143		Value::List(items) => ("List", format!("{}", Value::List(items.clone()))),
144		Value::Record(fields) => ("Record", format!("{}", Value::Record(fields.clone()))),
145		Value::Tuple(items) => ("Tuple", format!("{}", Value::Tuple(items.clone()))),
146	};
147	WireValue {
148		type_name: type_name.to_string(),
149		value: value_str,
150	}
151}
152
153#[cfg(any(feature = "http", feature = "ws"))]
154pub fn params_to_wire(params: Params) -> Option<WireParams> {
155	match params {
156		Params::None => None,
157		Params::Positional(values) => Some(WireParams::Positional(
158			Arc::unwrap_or_clone(values).into_iter().map(value_to_wire).collect(),
159		)),
160		Params::Named(map) => Some(WireParams::Named(
161			Arc::unwrap_or_clone(map).into_iter().map(|(k, v)| (k, value_to_wire(v))).collect(),
162		)),
163	}
164}
165
166#[cfg(any(feature = "http", feature = "ws"))]
167#[derive(Debug, Serialize, Deserialize)]
168pub struct Request {
169	pub id: String,
170	#[serde(flatten)]
171	pub payload: RequestPayload,
172}
173
174#[cfg(any(feature = "http", feature = "ws"))]
175#[derive(Debug, Serialize, Deserialize)]
176#[serde(tag = "type", content = "payload")]
177pub enum RequestPayload {
178	Auth(AuthRequest),
179	Admin(AdminRequest),
180	Command(CommandRequest),
181	Query(QueryRequest),
182	Subscribe(SubscribeRequest),
183	Unsubscribe(UnsubscribeRequest),
184	Logout,
185}
186
187#[cfg(any(feature = "http", feature = "ws"))]
188#[derive(Debug, Serialize, Deserialize)]
189pub struct AdminRequest {
190	pub statements: Vec<String>,
191	pub params: Option<WireParams>,
192}
193
194#[cfg(any(feature = "http", feature = "ws"))]
195#[derive(Debug, Serialize, Deserialize)]
196pub struct AuthRequest {
197	#[serde(skip_serializing_if = "Option::is_none")]
198	pub token: Option<String>,
199	#[serde(skip_serializing_if = "Option::is_none")]
200	pub method: Option<String>,
201	#[serde(skip_serializing_if = "Option::is_none")]
202	pub credentials: Option<HashMap<String, String>>,
203}
204
205#[cfg(any(feature = "http", feature = "ws"))]
206#[derive(Debug, Serialize, Deserialize)]
207pub struct CommandRequest {
208	pub statements: Vec<String>,
209	pub params: Option<WireParams>,
210}
211
212#[cfg(any(feature = "http", feature = "ws"))]
213#[derive(Debug, Serialize, Deserialize)]
214pub struct QueryRequest {
215	pub statements: Vec<String>,
216	pub params: Option<WireParams>,
217}
218
219#[cfg(any(feature = "http", feature = "ws"))]
220#[derive(Debug, Serialize, Deserialize)]
221pub struct SubscribeRequest {
222	pub query: String,
223}
224
225#[cfg(any(feature = "http", feature = "ws"))]
226#[derive(Debug, Serialize, Deserialize)]
227pub struct UnsubscribeRequest {
228	pub subscription_id: String,
229}
230
231#[cfg(any(feature = "http", feature = "ws"))]
232#[derive(Debug, Serialize, Deserialize)]
233pub struct Response {
234	pub id: String,
235	#[serde(flatten)]
236	pub payload: ResponsePayload,
237}
238
239#[cfg(any(feature = "http", feature = "ws"))]
240#[derive(Debug, Serialize, Deserialize)]
241#[serde(tag = "type", content = "payload")]
242pub enum ResponsePayload {
243	Auth(AuthResponse),
244	Err(ErrResponse),
245	Admin(AdminResponse),
246	Command(CommandResponse),
247	Query(QueryResponse),
248	Subscribed(SubscribedResponse),
249	Unsubscribed(UnsubscribedResponse),
250	Logout(LogoutResponsePayload),
251}
252
253#[cfg(any(feature = "http", feature = "ws"))]
254#[derive(Debug, Serialize, Deserialize)]
255pub struct AdminResponse {
256	pub content_type: String,
257	pub body: JsonValue,
258}
259
260#[cfg(any(feature = "http", feature = "ws"))]
261use reifydb_type::error::Diagnostic;
262
263#[cfg(any(feature = "http", feature = "ws"))]
264#[derive(Debug, Serialize, Deserialize)]
265pub struct AuthResponse {
266	#[serde(skip_serializing_if = "Option::is_none")]
267	pub status: Option<String>,
268	#[serde(skip_serializing_if = "Option::is_none")]
269	pub token: Option<String>,
270	#[serde(skip_serializing_if = "Option::is_none")]
271	pub identity: Option<String>,
272}
273
274#[cfg(any(feature = "http", feature = "ws"))]
275#[derive(Debug, Serialize, Deserialize)]
276pub struct ErrResponse {
277	pub diagnostic: Diagnostic,
278}
279
280#[cfg(any(feature = "http", feature = "ws"))]
281#[derive(Debug, Serialize, Deserialize)]
282pub struct CommandResponse {
283	pub content_type: String,
284	pub body: JsonValue,
285}
286
287#[cfg(any(feature = "http", feature = "ws"))]
288#[derive(Debug, Serialize, Deserialize)]
289pub struct QueryResponse {
290	pub content_type: String,
291	pub body: JsonValue,
292}
293
294#[cfg(any(feature = "http", feature = "ws"))]
295#[derive(Debug, Serialize, Deserialize)]
296pub struct SubscribedResponse {
297	pub subscription_id: String,
298}
299
300#[cfg(any(feature = "http", feature = "ws"))]
301#[derive(Debug, Serialize, Deserialize)]
302pub struct UnsubscribedResponse {
303	pub subscription_id: String,
304}
305
306#[cfg(any(feature = "http", feature = "ws"))]
307#[derive(Debug, Serialize, Deserialize)]
308pub struct LogoutResponsePayload {
309	pub status: String,
310}
311
312#[cfg(any(feature = "http", feature = "ws"))]
313#[derive(Debug, Clone, Serialize, Deserialize)]
314pub struct ClientFrame {
315	pub row_numbers: Vec<u64>,
316	#[serde(default)]
317	pub created_at: Vec<String>,
318	#[serde(default)]
319	pub updated_at: Vec<String>,
320	pub columns: Vec<ClientColumn>,
321}
322
323#[cfg(any(feature = "http", feature = "ws"))]
324#[derive(Debug, Clone, Serialize, Deserialize)]
325pub struct ClientColumn {
326	pub name: String,
327	pub r#type: Type,
328	pub payload: Vec<String>,
329}
330
331#[cfg(any(feature = "http", feature = "ws"))]
332/// Server-initiated push message (no request id).
333#[derive(Debug, Serialize, Deserialize)]
334#[serde(tag = "type", content = "payload")]
335pub enum ServerPush {
336	Change(ChangePayload),
337}
338
339#[cfg(any(feature = "http", feature = "ws"))]
340/// Payload for subscription change notifications.
341#[derive(Debug, Clone, Serialize, Deserialize)]
342pub struct ChangePayload {
343	pub subscription_id: String,
344	pub content_type: String,
345	pub body: JsonValue,
346}