Skip to main content

reifydb_client/
lib.rs

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