Skip to main content

reifydb_sub_server/
wire.rs

1// SPDX-License-Identifier: AGPL-3.0-or-later
2// Copyright (c) 2025 ReifyDB
3
4//! Wire format types for the WebSocket and HTTP protocol layers.
5//!
6//! Parameters are encoded in the following wire format:
7//! - Positional: `[{"type":"Int2","value":"1234"}, ...]`
8//! - Named: `{"key": {"type":"Int2","value":"1234"}, ...}`
9//!
10//! These types deserialize that format and convert it to the internal [`Params`] type.
11
12use std::collections::HashMap;
13
14use reifydb_type::{
15	fragment::Fragment,
16	params::Params,
17	value::{
18		Value,
19		blob::Blob,
20		decimal::parse::parse_decimal,
21		identity::IdentityId,
22		temporal::parse::{
23			date::parse_date, datetime::parse_datetime, duration::parse_duration, time::parse_time,
24		},
25		uuid::parse::{parse_uuid4, parse_uuid7},
26	},
27};
28use serde::{Deserialize, Serialize};
29
30/// Wire format for a single typed value: `{"type": "Int2", "value": "1234"}`
31#[derive(Debug, Serialize, Deserialize)]
32pub struct WireValue {
33	#[serde(rename = "type")]
34	pub type_name: String,
35	pub value: String,
36}
37
38/// Wire format for query parameters.
39///
40/// Either positional or named:
41/// - Positional: `[{"type":"Int2","value":"1234"}, ...]`
42/// - Named: `{"key": {"type":"Int2","value":"1234"}, ...}`
43#[derive(Debug, Serialize, Deserialize)]
44#[serde(untagged)]
45pub enum WireParams {
46	Positional(Vec<WireValue>),
47	Named(HashMap<String, WireValue>),
48}
49
50fn wire_value_to_value(wire: WireValue) -> Result<Value, String> {
51	let v = wire.value.as_str();
52	match wire.type_name.as_str() {
53		"None" => Ok(Value::none()),
54		"Boolean" => v
55			.parse::<bool>()
56			.map(Value::Boolean)
57			.map_err(|e| format!("invalid Boolean value '{}': {}", v, e)),
58		"Float4" => {
59			v.parse::<f32>().map(Value::float4).map_err(|e| format!("invalid Float4 value '{}': {}", v, e))
60		}
61		"Float8" => {
62			v.parse::<f64>().map(Value::float8).map_err(|e| format!("invalid Float8 value '{}': {}", v, e))
63		}
64		"Int1" => v.parse::<i8>().map(Value::Int1).map_err(|e| format!("invalid Int1 value '{}': {}", v, e)),
65		"Int2" => v.parse::<i16>().map(Value::Int2).map_err(|e| format!("invalid Int2 value '{}': {}", v, e)),
66		"Int4" => v.parse::<i32>().map(Value::Int4).map_err(|e| format!("invalid Int4 value '{}': {}", v, e)),
67		"Int8" => v.parse::<i64>().map(Value::Int8).map_err(|e| format!("invalid Int8 value '{}': {}", v, e)),
68		"Int16" => {
69			v.parse::<i128>().map(Value::Int16).map_err(|e| format!("invalid Int16 value '{}': {}", v, e))
70		}
71		"Utf8" => Ok(Value::Utf8(v.to_string())),
72		"Uint1" => v.parse::<u8>().map(Value::Uint1).map_err(|e| format!("invalid Uint1 value '{}': {}", v, e)),
73		"Uint2" => {
74			v.parse::<u16>().map(Value::Uint2).map_err(|e| format!("invalid Uint2 value '{}': {}", v, e))
75		}
76		"Uint4" => {
77			v.parse::<u32>().map(Value::Uint4).map_err(|e| format!("invalid Uint4 value '{}': {}", v, e))
78		}
79		"Uint8" => {
80			v.parse::<u64>().map(Value::Uint8).map_err(|e| format!("invalid Uint8 value '{}': {}", v, e))
81		}
82		"Uint16" => {
83			v.parse::<u128>().map(Value::Uint16).map_err(|e| format!("invalid Uint16 value '{}': {}", v, e))
84		}
85		"Uuid4" => parse_uuid4(Fragment::internal(v))
86			.map(Value::Uuid4)
87			.map_err(|e| format!("invalid Uuid4 value '{}': {:?}", v, e)),
88		"Uuid7" => parse_uuid7(Fragment::internal(v))
89			.map(Value::Uuid7)
90			.map_err(|e| format!("invalid Uuid7 value '{}': {:?}", v, e)),
91		"Date" => parse_date(Fragment::internal(v))
92			.map(Value::Date)
93			.map_err(|e| format!("invalid Date value '{}': {:?}", v, e)),
94		"DateTime" => parse_datetime(Fragment::internal(v))
95			.map(Value::DateTime)
96			.map_err(|e| format!("invalid DateTime value '{}': {:?}", v, e)),
97		"Time" => parse_time(Fragment::internal(v))
98			.map(Value::Time)
99			.map_err(|e| format!("invalid Time value '{}': {:?}", v, e)),
100		"Duration" => parse_duration(Fragment::internal(v))
101			.map(Value::Duration)
102			.map_err(|e| format!("invalid Duration value '{}': {:?}", v, e)),
103		"Blob" => Blob::from_hex(Fragment::internal(v))
104			.map(Value::Blob)
105			.map_err(|e| format!("invalid Blob value '{}': {:?}", v, e)),
106		"Decimal" => parse_decimal(Fragment::internal(v))
107			.map(Value::Decimal)
108			.map_err(|e| format!("invalid Decimal value '{}': {:?}", v, e)),
109		"IdentityId" => parse_uuid7(Fragment::internal(v))
110			.map(|u| Value::IdentityId(IdentityId::new(u)))
111			.map_err(|e| format!("invalid IdentityId value '{}': {:?}", v, e)),
112		_ => Err(format!("unknown type '{}'", wire.type_name)),
113	}
114}
115
116impl WireParams {
117	pub fn into_params(self) -> Result<Params, String> {
118		match self {
119			WireParams::Positional(items) => {
120				let mut values = Vec::with_capacity(items.len());
121				for item in items {
122					values.push(wire_value_to_value(item)?);
123				}
124				Ok(Params::Positional(values))
125			}
126			WireParams::Named(map) => {
127				let mut result = HashMap::with_capacity(map.len());
128				for (key, wire) in map {
129					result.insert(key, wire_value_to_value(wire)?);
130				}
131				Ok(Params::Named(result))
132			}
133		}
134	}
135}