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