recurram 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +163 -0
- package/crates/recurram-bridge/Cargo.toml +13 -0
- package/crates/recurram-bridge/src/lib.rs +900 -0
- package/crates/recurram-napi/Cargo.toml +16 -0
- package/crates/recurram-napi/build.rs +3 -0
- package/crates/recurram-napi/src/lib.rs +230 -0
- package/crates/recurram-wasm/Cargo.toml +11 -0
- package/crates/recurram-wasm/src/lib.rs +98 -0
- package/dist/backend.d.ts +4 -0
- package/dist/backend.js +47 -0
- package/dist/index.d.ts +51 -0
- package/dist/index.js +152 -0
- package/dist/runtime/node-backend.d.ts +2 -0
- package/dist/runtime/node-backend.js +45 -0
- package/dist/runtime/types.d.ts +35 -0
- package/dist/runtime/types.js +1 -0
- package/dist/runtime/wasm-backend.d.ts +2 -0
- package/dist/runtime/wasm-backend.js +44 -0
- package/dist/transport.d.ts +39 -0
- package/dist/transport.js +341 -0
- package/dist/types.d.ts +30 -0
- package/dist/types.js +1 -0
- package/native/recurram_napi.node +0 -0
- package/package.json +65 -0
- package/scripts/copy-napi.mjs +27 -0
- package/scripts/verify-tag-version.mjs +20 -0
|
@@ -0,0 +1,900 @@
|
|
|
1
|
+
use std::fmt;
|
|
2
|
+
|
|
3
|
+
use base64::{engine::general_purpose::STANDARD as BASE64, Engine as _};
|
|
4
|
+
use recurram::model::SchemaField;
|
|
5
|
+
use recurram::{
|
|
6
|
+
create_session_encoder, decode, encode, encode_batch, encode_with_schema, RecurramError, Schema,
|
|
7
|
+
SessionEncoder, SessionOptions, UnknownReferencePolicy, Value,
|
|
8
|
+
};
|
|
9
|
+
use serde::de::{self, MapAccess, Visitor};
|
|
10
|
+
use serde::{Deserialize, Deserializer, Serialize};
|
|
11
|
+
use serde_json::value::RawValue;
|
|
12
|
+
|
|
13
|
+
// ── SIMD-JSON helpers ───────────────────────────────────────────────────────
|
|
14
|
+
// simd_json::from_slice requires &mut [u8]. We accept owned Strings from N-API
|
|
15
|
+
// and convert via into_bytes() — zero extra allocation.
|
|
16
|
+
|
|
17
|
+
/// Parse JSON using simd-json (fast SIMD path). Falls back gracefully since
|
|
18
|
+
/// simd_json implements serde's Deserializer trait.
|
|
19
|
+
#[inline(always)]
|
|
20
|
+
fn simd_from_mut_slice<'de, T: Deserialize<'de>>(bytes: &'de mut [u8]) -> Result<T> {
|
|
21
|
+
simd_json::from_slice(bytes)
|
|
22
|
+
.map_err(|e| BridgeError::new(format!("simd-json parse error: {e}")))
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
pub type Result<T> = std::result::Result<T, BridgeError>;
|
|
26
|
+
|
|
27
|
+
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
28
|
+
pub struct BridgeError {
|
|
29
|
+
message: String,
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
impl BridgeError {
|
|
33
|
+
fn new(message: impl Into<String>) -> Self {
|
|
34
|
+
Self {
|
|
35
|
+
message: message.into(),
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
impl fmt::Display for BridgeError {
|
|
41
|
+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
42
|
+
write!(f, "{}", self.message)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
impl std::error::Error for BridgeError {}
|
|
47
|
+
|
|
48
|
+
impl From<serde_json::Error> for BridgeError {
|
|
49
|
+
fn from(value: serde_json::Error) -> Self {
|
|
50
|
+
Self::new(format!("invalid json payload: {value}"))
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
impl From<RecurramError> for BridgeError {
|
|
55
|
+
fn from(value: RecurramError) -> Self {
|
|
56
|
+
Self::new(value.to_string())
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
61
|
+
#[serde(tag = "t", content = "v")]
|
|
62
|
+
pub enum TransportValue {
|
|
63
|
+
#[serde(rename = "null")]
|
|
64
|
+
Null,
|
|
65
|
+
#[serde(rename = "bool")]
|
|
66
|
+
Bool(bool),
|
|
67
|
+
#[serde(rename = "i64")]
|
|
68
|
+
I64(String),
|
|
69
|
+
#[serde(rename = "u64")]
|
|
70
|
+
U64(String),
|
|
71
|
+
#[serde(rename = "f64")]
|
|
72
|
+
F64(f64),
|
|
73
|
+
#[serde(rename = "string")]
|
|
74
|
+
String(String),
|
|
75
|
+
#[serde(rename = "binary")]
|
|
76
|
+
Binary(String),
|
|
77
|
+
#[serde(rename = "array")]
|
|
78
|
+
Array(Vec<TransportValue>),
|
|
79
|
+
#[serde(rename = "map")]
|
|
80
|
+
Map(Vec<(String, TransportValue)>),
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
#[derive(Debug, Clone, Deserialize)]
|
|
84
|
+
#[serde(untagged)]
|
|
85
|
+
enum U64Like {
|
|
86
|
+
Number(u64),
|
|
87
|
+
String(String),
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
impl U64Like {
|
|
91
|
+
fn parse(self, field_name: &'static str) -> Result<u64> {
|
|
92
|
+
match self {
|
|
93
|
+
Self::Number(value) => Ok(value),
|
|
94
|
+
Self::String(raw) => raw
|
|
95
|
+
.parse::<u64>()
|
|
96
|
+
.map_err(|_| BridgeError::new(format!("invalid {field_name}: expected u64"))),
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
#[derive(Debug, Clone, Deserialize)]
|
|
102
|
+
#[serde(untagged)]
|
|
103
|
+
enum I64Like {
|
|
104
|
+
Number(i64),
|
|
105
|
+
String(String),
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
impl I64Like {
|
|
109
|
+
fn parse(self, field_name: &'static str) -> Result<i64> {
|
|
110
|
+
match self {
|
|
111
|
+
Self::Number(value) => Ok(value),
|
|
112
|
+
Self::String(raw) => raw
|
|
113
|
+
.parse::<i64>()
|
|
114
|
+
.map_err(|_| BridgeError::new(format!("invalid {field_name}: expected i64"))),
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
#[derive(Debug, Clone, Deserialize)]
|
|
120
|
+
#[serde(rename_all = "camelCase")]
|
|
121
|
+
struct TransportSchema {
|
|
122
|
+
schema_id: U64Like,
|
|
123
|
+
name: String,
|
|
124
|
+
fields: Vec<TransportSchemaField>,
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
#[derive(Debug, Clone, Deserialize)]
|
|
128
|
+
#[serde(rename_all = "camelCase")]
|
|
129
|
+
struct TransportSchemaField {
|
|
130
|
+
number: U64Like,
|
|
131
|
+
name: String,
|
|
132
|
+
logical_type: String,
|
|
133
|
+
required: bool,
|
|
134
|
+
#[serde(default)]
|
|
135
|
+
default_value: Option<TransportValue>,
|
|
136
|
+
#[serde(default)]
|
|
137
|
+
min: Option<I64Like>,
|
|
138
|
+
#[serde(default)]
|
|
139
|
+
max: Option<I64Like>,
|
|
140
|
+
#[serde(default)]
|
|
141
|
+
enum_values: Vec<String>,
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
#[derive(Debug, Clone, Deserialize, Default)]
|
|
145
|
+
#[serde(rename_all = "camelCase")]
|
|
146
|
+
struct TransportSessionOptions {
|
|
147
|
+
#[serde(default)]
|
|
148
|
+
max_base_snapshots: Option<usize>,
|
|
149
|
+
#[serde(default)]
|
|
150
|
+
enable_state_patch: Option<bool>,
|
|
151
|
+
#[serde(default)]
|
|
152
|
+
enable_template_batch: Option<bool>,
|
|
153
|
+
#[serde(default)]
|
|
154
|
+
enable_trained_dictionary: Option<bool>,
|
|
155
|
+
#[serde(default)]
|
|
156
|
+
unknown_reference_policy: Option<String>,
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
pub fn encode_transport_json(mut value_json: String) -> Result<Vec<u8>> {
|
|
160
|
+
let bytes = unsafe { value_json.as_bytes_mut() };
|
|
161
|
+
let transport: TransportValue = simd_from_mut_slice(bytes)?;
|
|
162
|
+
let value = transport_to_value(transport)?;
|
|
163
|
+
encode(&value).map_err(Into::into)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
pub fn decode_to_transport_json(bytes: &[u8]) -> Result<String> {
|
|
167
|
+
let value = decode(bytes)?;
|
|
168
|
+
let transport = value_to_transport(value);
|
|
169
|
+
serde_json::to_string(&transport).map_err(Into::into)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
pub fn decode_to_compact_json(bytes: &[u8]) -> Result<String> {
|
|
173
|
+
let value = decode(bytes)?;
|
|
174
|
+
let mut out = String::with_capacity(256);
|
|
175
|
+
value_to_compact_json_str(&value, &mut out);
|
|
176
|
+
Ok(out)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
pub fn encode_with_schema_transport_json(
|
|
180
|
+
mut schema_json: String,
|
|
181
|
+
mut value_json: String,
|
|
182
|
+
) -> Result<Vec<u8>> {
|
|
183
|
+
let schema = parse_schema_json(&mut schema_json)?;
|
|
184
|
+
let vbytes = unsafe { value_json.as_bytes_mut() };
|
|
185
|
+
let transport: TransportValue = simd_from_mut_slice(vbytes)?;
|
|
186
|
+
let value = transport_to_value(transport)?;
|
|
187
|
+
encode_with_schema(&schema, &value).map_err(Into::into)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
pub fn encode_batch_transport_json(mut values_json: String) -> Result<Vec<u8>> {
|
|
191
|
+
let bytes = unsafe { values_json.as_bytes_mut() };
|
|
192
|
+
let transports: Vec<TransportValue> = simd_from_mut_slice(bytes)?;
|
|
193
|
+
let values: Vec<Value> = transports
|
|
194
|
+
.into_iter()
|
|
195
|
+
.map(transport_to_value)
|
|
196
|
+
.collect::<Result<Vec<_>>>()?;
|
|
197
|
+
encode_batch(&values).map_err(Into::into)
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
pub struct BridgeSessionEncoder {
|
|
201
|
+
inner: SessionEncoder,
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
impl BridgeSessionEncoder {
|
|
205
|
+
pub fn new(options_json: Option<&str>) -> Result<Self> {
|
|
206
|
+
let options = parse_session_options_json(options_json)?;
|
|
207
|
+
Ok(Self {
|
|
208
|
+
inner: create_session_encoder(options),
|
|
209
|
+
})
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
pub fn encode_transport_json(&mut self, mut value_json: String) -> Result<Vec<u8>> {
|
|
213
|
+
let bytes = unsafe { value_json.as_bytes_mut() };
|
|
214
|
+
let transport: TransportValue = simd_from_mut_slice(bytes)?;
|
|
215
|
+
let value = transport_to_value(transport)?;
|
|
216
|
+
self.inner.encode(&value).map_err(Into::into)
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
pub fn encode_with_schema_transport_json(
|
|
220
|
+
&mut self,
|
|
221
|
+
mut schema_json: String,
|
|
222
|
+
mut value_json: String,
|
|
223
|
+
) -> Result<Vec<u8>> {
|
|
224
|
+
let schema = parse_schema_json(&mut schema_json)?;
|
|
225
|
+
let vbytes = unsafe { value_json.as_bytes_mut() };
|
|
226
|
+
let transport: TransportValue = simd_from_mut_slice(vbytes)?;
|
|
227
|
+
let value = transport_to_value(transport)?;
|
|
228
|
+
self.inner
|
|
229
|
+
.encode_with_schema(&schema, &value)
|
|
230
|
+
.map_err(Into::into)
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
pub fn encode_batch_transport_json(&mut self, mut values_json: String) -> Result<Vec<u8>> {
|
|
234
|
+
let bytes = unsafe { values_json.as_bytes_mut() };
|
|
235
|
+
let transports: Vec<TransportValue> = simd_from_mut_slice(bytes)?;
|
|
236
|
+
let values: Vec<Value> = transports
|
|
237
|
+
.into_iter()
|
|
238
|
+
.map(transport_to_value)
|
|
239
|
+
.collect::<Result<Vec<_>>>()?;
|
|
240
|
+
self.inner.encode_batch(&values).map_err(Into::into)
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
pub fn encode_patch_transport_json(&mut self, mut value_json: String) -> Result<Vec<u8>> {
|
|
244
|
+
let bytes = unsafe { value_json.as_bytes_mut() };
|
|
245
|
+
let transport: TransportValue = simd_from_mut_slice(bytes)?;
|
|
246
|
+
let value = transport_to_value(transport)?;
|
|
247
|
+
self.inner.encode_patch(&value).map_err(Into::into)
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
pub fn encode_micro_batch_transport_json(
|
|
251
|
+
&mut self,
|
|
252
|
+
mut values_json: String,
|
|
253
|
+
) -> Result<Vec<u8>> {
|
|
254
|
+
let bytes = unsafe { values_json.as_bytes_mut() };
|
|
255
|
+
let transports: Vec<TransportValue> = simd_from_mut_slice(bytes)?;
|
|
256
|
+
let values: Vec<Value> = transports
|
|
257
|
+
.into_iter()
|
|
258
|
+
.map(transport_to_value)
|
|
259
|
+
.collect::<Result<Vec<_>>>()?;
|
|
260
|
+
self.inner.encode_micro_batch(&values).map_err(Into::into)
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
pub fn reset(&mut self) {
|
|
264
|
+
self.inner.reset();
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
fn parse_schema_json(schema_json: &mut String) -> Result<Schema> {
|
|
269
|
+
let bytes = unsafe { schema_json.as_bytes_mut() };
|
|
270
|
+
let schema: TransportSchema = simd_from_mut_slice(bytes)?;
|
|
271
|
+
transport_schema_to_schema(schema)
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
fn parse_session_options_json(options_json: Option<&str>) -> Result<SessionOptions> {
|
|
275
|
+
let Some(raw) = options_json else {
|
|
276
|
+
return Ok(SessionOptions::default());
|
|
277
|
+
};
|
|
278
|
+
// Session options parsing is cold path, use serde_json
|
|
279
|
+
let options: TransportSessionOptions = serde_json::from_str(raw)?;
|
|
280
|
+
transport_session_options_to_options(options)
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
fn transport_schema_to_schema(schema: TransportSchema) -> Result<Schema> {
|
|
284
|
+
let schema_id = schema.schema_id.parse("schemaId")?;
|
|
285
|
+
let fields = schema
|
|
286
|
+
.fields
|
|
287
|
+
.into_iter()
|
|
288
|
+
.map(|field| {
|
|
289
|
+
Ok(SchemaField {
|
|
290
|
+
number: field.number.parse("field.number")?,
|
|
291
|
+
name: field.name,
|
|
292
|
+
logical_type: field.logical_type,
|
|
293
|
+
required: field.required,
|
|
294
|
+
default_value: field.default_value.map(transport_to_value).transpose()?,
|
|
295
|
+
min: field.min.map(|v| v.parse("field.min")).transpose()?,
|
|
296
|
+
max: field.max.map(|v| v.parse("field.max")).transpose()?,
|
|
297
|
+
enum_values: field.enum_values,
|
|
298
|
+
})
|
|
299
|
+
})
|
|
300
|
+
.collect::<Result<Vec<_>>>()?;
|
|
301
|
+
Ok(Schema {
|
|
302
|
+
schema_id,
|
|
303
|
+
name: schema.name,
|
|
304
|
+
fields,
|
|
305
|
+
})
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
fn transport_session_options_to_options(
|
|
309
|
+
options: TransportSessionOptions,
|
|
310
|
+
) -> Result<SessionOptions> {
|
|
311
|
+
let mut parsed = SessionOptions::default();
|
|
312
|
+
if let Some(value) = options.max_base_snapshots {
|
|
313
|
+
parsed.max_base_snapshots = value;
|
|
314
|
+
}
|
|
315
|
+
if let Some(value) = options.enable_state_patch {
|
|
316
|
+
parsed.enable_state_patch = value;
|
|
317
|
+
}
|
|
318
|
+
if let Some(value) = options.enable_template_batch {
|
|
319
|
+
parsed.enable_template_batch = value;
|
|
320
|
+
}
|
|
321
|
+
if let Some(value) = options.enable_trained_dictionary {
|
|
322
|
+
parsed.enable_trained_dictionary = value;
|
|
323
|
+
}
|
|
324
|
+
if let Some(policy) = options.unknown_reference_policy {
|
|
325
|
+
parsed.unknown_reference_policy = parse_unknown_reference_policy(&policy)?;
|
|
326
|
+
}
|
|
327
|
+
Ok(parsed)
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
fn parse_unknown_reference_policy(value: &str) -> Result<UnknownReferencePolicy> {
|
|
331
|
+
match value {
|
|
332
|
+
"failFast" | "fail_fast" | "FailFast" => Ok(UnknownReferencePolicy::FailFast),
|
|
333
|
+
"statelessRetry" | "stateless_retry" | "StatelessRetry" => {
|
|
334
|
+
Ok(UnknownReferencePolicy::StatelessRetry)
|
|
335
|
+
}
|
|
336
|
+
_ => Err(BridgeError::new(
|
|
337
|
+
"unknownReferencePolicy must be failFast or statelessRetry",
|
|
338
|
+
)),
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// ── Direct API (bypasses JSON string intermediate) ──────────────────────────
|
|
343
|
+
|
|
344
|
+
pub fn encode_direct(transport: TransportValue) -> Result<Vec<u8>> {
|
|
345
|
+
let value = transport_to_value(transport)?;
|
|
346
|
+
encode(&value).map_err(Into::into)
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
pub fn encode_direct_from_json(jv: serde_json::Value) -> Result<Vec<u8>> {
|
|
350
|
+
let value = parse_value_from_json_value(jv)?;
|
|
351
|
+
encode(&value).map_err(Into::into)
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
pub fn decode_direct(bytes: &[u8]) -> Result<TransportValue> {
|
|
355
|
+
let value = decode(bytes)?;
|
|
356
|
+
Ok(value_to_transport(value))
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
pub fn encode_batch_direct(transports: Vec<TransportValue>) -> Result<Vec<u8>> {
|
|
360
|
+
let values: Vec<Value> = transports
|
|
361
|
+
.into_iter()
|
|
362
|
+
.map(transport_to_value)
|
|
363
|
+
.collect::<Result<Vec<_>>>()?;
|
|
364
|
+
encode_batch(&values).map_err(Into::into)
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
pub fn encode_batch_direct_from_json(jv: serde_json::Value) -> Result<Vec<u8>> {
|
|
368
|
+
let values = parse_values_from_json_value(jv)?;
|
|
369
|
+
encode_batch(&values).map_err(Into::into)
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
impl BridgeSessionEncoder {
|
|
373
|
+
pub fn encode_direct(&mut self, transport: TransportValue) -> Result<Vec<u8>> {
|
|
374
|
+
let value = transport_to_value(transport)?;
|
|
375
|
+
self.inner.encode(&value).map_err(Into::into)
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
pub fn encode_direct_from_json(&mut self, jv: serde_json::Value) -> Result<Vec<u8>> {
|
|
379
|
+
let value = parse_value_from_json_value(jv)?;
|
|
380
|
+
self.inner.encode(&value).map_err(Into::into)
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
pub fn encode_batch_direct(&mut self, transports: Vec<TransportValue>) -> Result<Vec<u8>> {
|
|
384
|
+
let values: Vec<Value> = transports
|
|
385
|
+
.into_iter()
|
|
386
|
+
.map(transport_to_value)
|
|
387
|
+
.collect::<Result<Vec<_>>>()?;
|
|
388
|
+
self.inner.encode_batch(&values).map_err(Into::into)
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
pub fn encode_batch_direct_from_json(&mut self, jv: serde_json::Value) -> Result<Vec<u8>> {
|
|
392
|
+
let values = parse_values_from_json_value(jv)?;
|
|
393
|
+
self.inner.encode_batch(&values).map_err(Into::into)
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
pub fn encode_patch_direct(&mut self, transport: TransportValue) -> Result<Vec<u8>> {
|
|
397
|
+
let value = transport_to_value(transport)?;
|
|
398
|
+
self.inner.encode_patch(&value).map_err(Into::into)
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
pub fn encode_patch_direct_from_json(&mut self, jv: serde_json::Value) -> Result<Vec<u8>> {
|
|
402
|
+
let value = parse_value_from_json_value(jv)?;
|
|
403
|
+
self.inner.encode_patch(&value).map_err(Into::into)
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
pub fn encode_micro_batch_direct(
|
|
407
|
+
&mut self,
|
|
408
|
+
transports: Vec<TransportValue>,
|
|
409
|
+
) -> Result<Vec<u8>> {
|
|
410
|
+
let values: Vec<Value> = transports
|
|
411
|
+
.into_iter()
|
|
412
|
+
.map(transport_to_value)
|
|
413
|
+
.collect::<Result<Vec<_>>>()?;
|
|
414
|
+
self.inner.encode_micro_batch(&values).map_err(Into::into)
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
pub fn encode_micro_batch_direct_from_json(
|
|
418
|
+
&mut self,
|
|
419
|
+
jv: serde_json::Value,
|
|
420
|
+
) -> Result<Vec<u8>> {
|
|
421
|
+
let values = parse_values_from_json_value(jv)?;
|
|
422
|
+
self.inner.encode_micro_batch(&values).map_err(Into::into)
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
fn transport_to_value(value: TransportValue) -> Result<Value> {
|
|
427
|
+
match value {
|
|
428
|
+
TransportValue::Null => Ok(Value::Null),
|
|
429
|
+
TransportValue::Bool(v) => Ok(Value::Bool(v)),
|
|
430
|
+
TransportValue::I64(raw) => raw
|
|
431
|
+
.parse::<i64>()
|
|
432
|
+
.map(Value::I64)
|
|
433
|
+
.map_err(|_| BridgeError::new("invalid i64 value")),
|
|
434
|
+
TransportValue::U64(raw) => raw
|
|
435
|
+
.parse::<u64>()
|
|
436
|
+
.map(Value::U64)
|
|
437
|
+
.map_err(|_| BridgeError::new("invalid u64 value")),
|
|
438
|
+
TransportValue::F64(v) => Ok(Value::F64(v)),
|
|
439
|
+
TransportValue::String(v) => Ok(Value::String(v)),
|
|
440
|
+
TransportValue::Binary(raw) => BASE64
|
|
441
|
+
.decode(raw)
|
|
442
|
+
.map(Value::Binary)
|
|
443
|
+
.map_err(|_| BridgeError::new("invalid base64 binary payload")),
|
|
444
|
+
TransportValue::Array(values) => values
|
|
445
|
+
.into_iter()
|
|
446
|
+
.map(transport_to_value)
|
|
447
|
+
.collect::<Result<Vec<_>>>()
|
|
448
|
+
.map(Value::Array),
|
|
449
|
+
TransportValue::Map(entries) => entries
|
|
450
|
+
.into_iter()
|
|
451
|
+
.map(|(k, v)| transport_to_value(v).map(|vv| (k, vv)))
|
|
452
|
+
.collect::<Result<Vec<_>>>()
|
|
453
|
+
.map(Value::Map),
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
fn value_to_transport(value: Value) -> TransportValue {
|
|
458
|
+
match value {
|
|
459
|
+
Value::Null => TransportValue::Null,
|
|
460
|
+
Value::Bool(v) => TransportValue::Bool(v),
|
|
461
|
+
Value::I64(v) => TransportValue::I64(v.to_string()),
|
|
462
|
+
Value::U64(v) => TransportValue::U64(v.to_string()),
|
|
463
|
+
Value::F64(v) => TransportValue::F64(v),
|
|
464
|
+
Value::String(v) => TransportValue::String(v),
|
|
465
|
+
Value::Binary(v) => TransportValue::Binary(BASE64.encode(v)),
|
|
466
|
+
Value::Array(values) => {
|
|
467
|
+
TransportValue::Array(values.into_iter().map(value_to_transport).collect())
|
|
468
|
+
}
|
|
469
|
+
Value::Map(entries) => TransportValue::Map(
|
|
470
|
+
entries
|
|
471
|
+
.into_iter()
|
|
472
|
+
.map(|(k, v)| (k, value_to_transport(v)))
|
|
473
|
+
.collect(),
|
|
474
|
+
),
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// ── Compact transport format ────────────────────────────────────────────────
|
|
479
|
+
//
|
|
480
|
+
// Tags: 0=null, 1=bool, 2=i64, 3=u64, 4=f64, 5=string, 6=binary, 7=array, 8=map
|
|
481
|
+
// Format: [tag] for null, [tag, value] for everything else.
|
|
482
|
+
// Map value is a flat array: [key1, val1, key2, val2, ...]
|
|
483
|
+
//
|
|
484
|
+
// Uses a custom serde Deserialize impl for single-pass JSON → Value conversion.
|
|
485
|
+
|
|
486
|
+
/// Serialize a recurram::Value to compact JSON format, writing directly to a String.
|
|
487
|
+
/// This avoids building any intermediate serde or transport objects.
|
|
488
|
+
fn value_to_compact_json_str(value: &Value, out: &mut String) {
|
|
489
|
+
match value {
|
|
490
|
+
Value::Null => out.push_str("[0]"),
|
|
491
|
+
Value::Bool(true) => out.push_str("[1,true]"),
|
|
492
|
+
Value::Bool(false) => out.push_str("[1,false]"),
|
|
493
|
+
Value::I64(v) => {
|
|
494
|
+
out.push_str("[2,\"");
|
|
495
|
+
let mut buf = itoa::Buffer::new();
|
|
496
|
+
out.push_str(buf.format(*v));
|
|
497
|
+
out.push_str("\"]");
|
|
498
|
+
}
|
|
499
|
+
Value::U64(v) => {
|
|
500
|
+
out.push_str("[3,\"");
|
|
501
|
+
let mut buf = itoa::Buffer::new();
|
|
502
|
+
out.push_str(buf.format(*v));
|
|
503
|
+
out.push_str("\"]");
|
|
504
|
+
}
|
|
505
|
+
Value::F64(v) => {
|
|
506
|
+
out.push_str("[4,");
|
|
507
|
+
// Use ryu for fast f64 formatting
|
|
508
|
+
let mut buf = ryu::Buffer::new();
|
|
509
|
+
out.push_str(buf.format(*v));
|
|
510
|
+
out.push(']');
|
|
511
|
+
}
|
|
512
|
+
Value::String(s) => {
|
|
513
|
+
out.push_str("[5,");
|
|
514
|
+
// JSON-escape the string
|
|
515
|
+
write_json_string(s, out);
|
|
516
|
+
out.push(']');
|
|
517
|
+
}
|
|
518
|
+
Value::Binary(b) => {
|
|
519
|
+
out.push_str("[6,\"");
|
|
520
|
+
out.push_str(&BASE64.encode(b));
|
|
521
|
+
out.push_str("\"]");
|
|
522
|
+
}
|
|
523
|
+
Value::Array(items) => {
|
|
524
|
+
out.push_str("[7,[");
|
|
525
|
+
for (i, item) in items.iter().enumerate() {
|
|
526
|
+
if i > 0 {
|
|
527
|
+
out.push(',');
|
|
528
|
+
}
|
|
529
|
+
value_to_compact_json_str(item, out);
|
|
530
|
+
}
|
|
531
|
+
out.push_str("]]");
|
|
532
|
+
}
|
|
533
|
+
Value::Map(entries) => {
|
|
534
|
+
out.push_str("[8,[");
|
|
535
|
+
for (i, (key, val)) in entries.iter().enumerate() {
|
|
536
|
+
if i > 0 {
|
|
537
|
+
out.push(',');
|
|
538
|
+
}
|
|
539
|
+
write_json_string(key, out);
|
|
540
|
+
out.push(',');
|
|
541
|
+
value_to_compact_json_str(val, out);
|
|
542
|
+
}
|
|
543
|
+
out.push_str("]]");
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
/// Write a JSON-escaped string (with surrounding quotes) to the output.
|
|
549
|
+
/// Optimized for the common case of ASCII strings with no special characters.
|
|
550
|
+
#[inline]
|
|
551
|
+
fn write_json_string(s: &str, out: &mut String) {
|
|
552
|
+
out.push('"');
|
|
553
|
+
let bytes = s.as_bytes();
|
|
554
|
+
let mut start = 0;
|
|
555
|
+
for (i, &b) in bytes.iter().enumerate() {
|
|
556
|
+
let escape = match b {
|
|
557
|
+
b'"' => "\\\"",
|
|
558
|
+
b'\\' => "\\\\",
|
|
559
|
+
b'\n' => "\\n",
|
|
560
|
+
b'\r' => "\\r",
|
|
561
|
+
b'\t' => "\\t",
|
|
562
|
+
b if b < 0x20 => {
|
|
563
|
+
// Flush any buffered bytes
|
|
564
|
+
if start < i {
|
|
565
|
+
out.push_str(&s[start..i]);
|
|
566
|
+
}
|
|
567
|
+
// Write \u00XX escape
|
|
568
|
+
static HEX: &[u8; 16] = b"0123456789abcdef";
|
|
569
|
+
let hi = HEX[(b >> 4) as usize] as char;
|
|
570
|
+
let lo = HEX[(b & 0xf) as usize] as char;
|
|
571
|
+
out.push_str("\\u00");
|
|
572
|
+
out.push(hi);
|
|
573
|
+
out.push(lo);
|
|
574
|
+
start = i + 1;
|
|
575
|
+
continue;
|
|
576
|
+
}
|
|
577
|
+
_ => {
|
|
578
|
+
continue;
|
|
579
|
+
}
|
|
580
|
+
};
|
|
581
|
+
// Flush any buffered bytes before the escape
|
|
582
|
+
if start < i {
|
|
583
|
+
out.push_str(&s[start..i]);
|
|
584
|
+
}
|
|
585
|
+
out.push_str(escape);
|
|
586
|
+
start = i + 1;
|
|
587
|
+
}
|
|
588
|
+
// Flush remaining
|
|
589
|
+
if start < bytes.len() {
|
|
590
|
+
out.push_str(&s[start..]);
|
|
591
|
+
}
|
|
592
|
+
out.push('"');
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
struct CompactValue(Value);
|
|
596
|
+
|
|
597
|
+
impl<'de> Deserialize<'de> for CompactValue {
|
|
598
|
+
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> std::result::Result<Self, D::Error> {
|
|
599
|
+
deserializer
|
|
600
|
+
.deserialize_seq(CompactValueVisitor)
|
|
601
|
+
.map(CompactValue)
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
struct CompactValueVisitor;
|
|
606
|
+
|
|
607
|
+
impl<'de> Visitor<'de> for CompactValueVisitor {
|
|
608
|
+
type Value = Value;
|
|
609
|
+
|
|
610
|
+
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
|
611
|
+
formatter.write_str("a compact value array [tag, value?]")
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
fn visit_seq<A: de::SeqAccess<'de>>(self, mut seq: A) -> std::result::Result<Value, A::Error> {
|
|
615
|
+
let tag: u64 = seq
|
|
616
|
+
.next_element()?
|
|
617
|
+
.ok_or_else(|| de::Error::custom("compact value missing tag"))?;
|
|
618
|
+
|
|
619
|
+
match tag {
|
|
620
|
+
0 => {
|
|
621
|
+
// null — no content element
|
|
622
|
+
Ok(Value::Null)
|
|
623
|
+
}
|
|
624
|
+
1 => {
|
|
625
|
+
// bool
|
|
626
|
+
let v: bool = seq
|
|
627
|
+
.next_element()?
|
|
628
|
+
.ok_or_else(|| de::Error::custom("compact bool missing value"))?;
|
|
629
|
+
Ok(Value::Bool(v))
|
|
630
|
+
}
|
|
631
|
+
2 => {
|
|
632
|
+
// i64 (as string)
|
|
633
|
+
let s: &str = seq
|
|
634
|
+
.next_element()?
|
|
635
|
+
.ok_or_else(|| de::Error::custom("compact i64 missing value"))?;
|
|
636
|
+
let v: i64 = s
|
|
637
|
+
.parse()
|
|
638
|
+
.map_err(|_| de::Error::custom("compact i64 invalid"))?;
|
|
639
|
+
Ok(Value::I64(v))
|
|
640
|
+
}
|
|
641
|
+
3 => {
|
|
642
|
+
// u64 (as string)
|
|
643
|
+
let s: &str = seq
|
|
644
|
+
.next_element()?
|
|
645
|
+
.ok_or_else(|| de::Error::custom("compact u64 missing value"))?;
|
|
646
|
+
let v: u64 = s
|
|
647
|
+
.parse()
|
|
648
|
+
.map_err(|_| de::Error::custom("compact u64 invalid"))?;
|
|
649
|
+
Ok(Value::U64(v))
|
|
650
|
+
}
|
|
651
|
+
4 => {
|
|
652
|
+
// f64
|
|
653
|
+
let v: f64 = seq
|
|
654
|
+
.next_element()?
|
|
655
|
+
.ok_or_else(|| de::Error::custom("compact f64 missing value"))?;
|
|
656
|
+
Ok(Value::F64(v))
|
|
657
|
+
}
|
|
658
|
+
5 => {
|
|
659
|
+
// string
|
|
660
|
+
let v: String = seq
|
|
661
|
+
.next_element()?
|
|
662
|
+
.ok_or_else(|| de::Error::custom("compact string missing value"))?;
|
|
663
|
+
Ok(Value::String(v))
|
|
664
|
+
}
|
|
665
|
+
6 => {
|
|
666
|
+
// binary (base64)
|
|
667
|
+
let encoded: &str = seq
|
|
668
|
+
.next_element()?
|
|
669
|
+
.ok_or_else(|| de::Error::custom("compact binary missing value"))?;
|
|
670
|
+
let bytes = BASE64
|
|
671
|
+
.decode(encoded)
|
|
672
|
+
.map_err(|_| de::Error::custom("compact binary invalid base64"))?;
|
|
673
|
+
Ok(Value::Binary(bytes))
|
|
674
|
+
}
|
|
675
|
+
7 => {
|
|
676
|
+
// array of CompactValues
|
|
677
|
+
let items: Vec<CompactValue> = seq
|
|
678
|
+
.next_element()?
|
|
679
|
+
.ok_or_else(|| de::Error::custom("compact array missing value"))?;
|
|
680
|
+
Ok(Value::Array(items.into_iter().map(|v| v.0).collect()))
|
|
681
|
+
}
|
|
682
|
+
8 => {
|
|
683
|
+
// map: flat array [key1, val1, key2, val2, ...]
|
|
684
|
+
// We deserialize this as a custom visitor that reads alternating string/CompactValue pairs
|
|
685
|
+
let entries: CompactMapEntries = seq
|
|
686
|
+
.next_element()?
|
|
687
|
+
.ok_or_else(|| de::Error::custom("compact map missing value"))?;
|
|
688
|
+
Ok(Value::Map(entries.0))
|
|
689
|
+
}
|
|
690
|
+
_ => Err(de::Error::custom(format!("compact unknown tag: {tag}"))),
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
/// Deserializes a flat array [key1, val1, key2, val2, ...] into Vec<(String, Value)>
|
|
696
|
+
struct CompactMapEntries(Vec<(String, Value)>);
|
|
697
|
+
|
|
698
|
+
impl<'de> Deserialize<'de> for CompactMapEntries {
|
|
699
|
+
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> std::result::Result<Self, D::Error> {
|
|
700
|
+
deserializer.deserialize_seq(CompactMapEntriesVisitor)
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
struct CompactMapEntriesVisitor;
|
|
705
|
+
|
|
706
|
+
impl<'de> Visitor<'de> for CompactMapEntriesVisitor {
|
|
707
|
+
type Value = CompactMapEntries;
|
|
708
|
+
|
|
709
|
+
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
|
710
|
+
formatter.write_str("a flat array of alternating key/value pairs")
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
fn visit_seq<A: de::SeqAccess<'de>>(
|
|
714
|
+
self,
|
|
715
|
+
mut seq: A,
|
|
716
|
+
) -> std::result::Result<CompactMapEntries, A::Error> {
|
|
717
|
+
let mut entries = Vec::with_capacity(seq.size_hint().unwrap_or(0) / 2);
|
|
718
|
+
loop {
|
|
719
|
+
let key: Option<String> = seq.next_element()?;
|
|
720
|
+
let Some(key) = key else { break };
|
|
721
|
+
let val: CompactValue = seq
|
|
722
|
+
.next_element()?
|
|
723
|
+
.ok_or_else(|| de::Error::custom("compact map missing value for key"))?;
|
|
724
|
+
entries.push((key, val.0));
|
|
725
|
+
}
|
|
726
|
+
Ok(CompactMapEntries(entries))
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
fn parse_compact_str(json: &mut [u8]) -> Result<Value> {
|
|
731
|
+
let compact: CompactValue = simd_json::from_slice(json)
|
|
732
|
+
.map_err(|e| BridgeError::new(format!("simd-json compact parse error: {e}")))?;
|
|
733
|
+
Ok(compact.0)
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
fn parse_compact_batch_str(json: &mut [u8]) -> Result<Vec<Value>> {
|
|
737
|
+
let items: Vec<CompactValue> = simd_json::from_slice(json)
|
|
738
|
+
.map_err(|e| BridgeError::new(format!("simd-json compact batch parse error: {e}")))?;
|
|
739
|
+
Ok(items.into_iter().map(|v| v.0).collect())
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
pub fn encode_compact_json(mut json: String) -> Result<Vec<u8>> {
|
|
743
|
+
let bytes = unsafe { json.as_bytes_mut() };
|
|
744
|
+
let value = parse_compact_str(bytes)?;
|
|
745
|
+
encode(&value).map_err(Into::into)
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
pub fn encode_batch_compact_json(mut json: String) -> Result<Vec<u8>> {
|
|
749
|
+
let bytes = unsafe { json.as_bytes_mut() };
|
|
750
|
+
let values = parse_compact_batch_str(bytes)?;
|
|
751
|
+
encode_batch(&values).map_err(Into::into)
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
impl BridgeSessionEncoder {
|
|
755
|
+
pub fn encode_compact_json(&mut self, mut json: String) -> Result<Vec<u8>> {
|
|
756
|
+
let bytes = unsafe { json.as_bytes_mut() };
|
|
757
|
+
let value = parse_compact_str(bytes)?;
|
|
758
|
+
self.inner.encode(&value).map_err(Into::into)
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
pub fn encode_batch_compact_json(&mut self, mut json: String) -> Result<Vec<u8>> {
|
|
762
|
+
let bytes = unsafe { json.as_bytes_mut() };
|
|
763
|
+
let values = parse_compact_batch_str(bytes)?;
|
|
764
|
+
self.inner.encode_batch(&values).map_err(Into::into)
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
pub fn encode_patch_compact_json(&mut self, mut json: String) -> Result<Vec<u8>> {
|
|
768
|
+
let bytes = unsafe { json.as_bytes_mut() };
|
|
769
|
+
let value = parse_compact_str(bytes)?;
|
|
770
|
+
self.inner.encode_patch(&value).map_err(Into::into)
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
pub fn encode_micro_batch_compact_json(&mut self, mut json: String) -> Result<Vec<u8>> {
|
|
774
|
+
let bytes = unsafe { json.as_bytes_mut() };
|
|
775
|
+
let values = parse_compact_batch_str(bytes)?;
|
|
776
|
+
self.inner.encode_micro_batch(&values).map_err(Into::into)
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
//
|
|
780
|
+
// Deserializes the transport JSON format directly into recurram::Value,
|
|
781
|
+
// skipping the TransportValue intermediate entirely. This avoids:
|
|
782
|
+
// 1. Allocating TransportValue tree
|
|
783
|
+
// 2. Walking the tree a second time in transport_to_value()
|
|
784
|
+
// 3. String allocations for i64/u64 intermediates
|
|
785
|
+
|
|
786
|
+
struct FastValueVisitor;
|
|
787
|
+
|
|
788
|
+
impl<'de> Visitor<'de> for FastValueVisitor {
|
|
789
|
+
type Value = Value;
|
|
790
|
+
|
|
791
|
+
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
|
792
|
+
formatter.write_str("a transport value object with 't' and optional 'v' fields")
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> std::result::Result<Value, A::Error> {
|
|
796
|
+
let mut tag: Option<String> = None;
|
|
797
|
+
let mut content_value: Option<Box<RawValue>> = None;
|
|
798
|
+
|
|
799
|
+
while let Some(key) = map.next_key::<String>()? {
|
|
800
|
+
match key.as_str() {
|
|
801
|
+
"t" => {
|
|
802
|
+
tag = Some(map.next_value()?);
|
|
803
|
+
}
|
|
804
|
+
"v" => {
|
|
805
|
+
content_value = Some(map.next_value()?);
|
|
806
|
+
}
|
|
807
|
+
_ => {
|
|
808
|
+
map.next_value::<serde::de::IgnoredAny>()?;
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
let tag = tag.ok_or_else(|| de::Error::missing_field("t"))?;
|
|
814
|
+
|
|
815
|
+
match tag.as_str() {
|
|
816
|
+
"null" => Ok(Value::Null),
|
|
817
|
+
"bool" => {
|
|
818
|
+
let raw = content_value.ok_or_else(|| de::Error::missing_field("v"))?;
|
|
819
|
+
let v: bool = serde_json::from_str(raw.get()).map_err(|e| de::Error::custom(e))?;
|
|
820
|
+
Ok(Value::Bool(v))
|
|
821
|
+
}
|
|
822
|
+
"i64" => {
|
|
823
|
+
let raw = content_value.ok_or_else(|| de::Error::missing_field("v"))?;
|
|
824
|
+
let s: &str = serde_json::from_str(raw.get()).map_err(|e| de::Error::custom(e))?;
|
|
825
|
+
let v: i64 = s.parse().map_err(|_| de::Error::custom("invalid i64"))?;
|
|
826
|
+
Ok(Value::I64(v))
|
|
827
|
+
}
|
|
828
|
+
"u64" => {
|
|
829
|
+
let raw = content_value.ok_or_else(|| de::Error::missing_field("v"))?;
|
|
830
|
+
let s: &str = serde_json::from_str(raw.get()).map_err(|e| de::Error::custom(e))?;
|
|
831
|
+
let v: u64 = s.parse().map_err(|_| de::Error::custom("invalid u64"))?;
|
|
832
|
+
Ok(Value::U64(v))
|
|
833
|
+
}
|
|
834
|
+
"f64" => {
|
|
835
|
+
let raw = content_value.ok_or_else(|| de::Error::missing_field("v"))?;
|
|
836
|
+
let v: f64 = serde_json::from_str(raw.get()).map_err(|e| de::Error::custom(e))?;
|
|
837
|
+
Ok(Value::F64(v))
|
|
838
|
+
}
|
|
839
|
+
"string" => {
|
|
840
|
+
let raw = content_value.ok_or_else(|| de::Error::missing_field("v"))?;
|
|
841
|
+
let v: String =
|
|
842
|
+
serde_json::from_str(raw.get()).map_err(|e| de::Error::custom(e))?;
|
|
843
|
+
Ok(Value::String(v))
|
|
844
|
+
}
|
|
845
|
+
"binary" => {
|
|
846
|
+
let raw = content_value.ok_or_else(|| de::Error::missing_field("v"))?;
|
|
847
|
+
let encoded: String =
|
|
848
|
+
serde_json::from_str(raw.get()).map_err(|e| de::Error::custom(e))?;
|
|
849
|
+
let bytes = BASE64
|
|
850
|
+
.decode(&encoded)
|
|
851
|
+
.map_err(|_| de::Error::custom("invalid base64"))?;
|
|
852
|
+
Ok(Value::Binary(bytes))
|
|
853
|
+
}
|
|
854
|
+
"array" => {
|
|
855
|
+
let raw = content_value.ok_or_else(|| de::Error::missing_field("v"))?;
|
|
856
|
+
let items: Vec<FastValue> =
|
|
857
|
+
serde_json::from_str(raw.get()).map_err(|e| de::Error::custom(e))?;
|
|
858
|
+
Ok(Value::Array(items.into_iter().map(|v| v.0).collect()))
|
|
859
|
+
}
|
|
860
|
+
"map" => {
|
|
861
|
+
let raw = content_value.ok_or_else(|| de::Error::missing_field("v"))?;
|
|
862
|
+
let entries: Vec<(String, FastValue)> =
|
|
863
|
+
serde_json::from_str(raw.get()).map_err(|e| de::Error::custom(e))?;
|
|
864
|
+
Ok(Value::Map(
|
|
865
|
+
entries.into_iter().map(|(k, v)| (k, v.0)).collect(),
|
|
866
|
+
))
|
|
867
|
+
}
|
|
868
|
+
other => Err(de::Error::unknown_variant(
|
|
869
|
+
other,
|
|
870
|
+
&[
|
|
871
|
+
"null", "bool", "i64", "u64", "f64", "string", "binary", "array", "map",
|
|
872
|
+
],
|
|
873
|
+
)),
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
/// A newtype wrapper around `Value` that implements `Deserialize` using the
|
|
879
|
+
/// fast single-pass visitor that converts transport JSON directly to `Value`.
|
|
880
|
+
struct FastValue(Value);
|
|
881
|
+
|
|
882
|
+
impl<'de> Deserialize<'de> for FastValue {
|
|
883
|
+
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> std::result::Result<Self, D::Error> {
|
|
884
|
+
deserializer
|
|
885
|
+
.deserialize_map(FastValueVisitor)
|
|
886
|
+
.map(FastValue)
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
/// Parse a serde_json::Value transport representation directly into recurram::Value.
|
|
891
|
+
fn parse_value_from_json_value(jv: serde_json::Value) -> Result<Value> {
|
|
892
|
+
let fast: FastValue = serde_json::from_value(jv)?;
|
|
893
|
+
Ok(fast.0)
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
/// Parse a serde_json::Value array of transport values.
|
|
897
|
+
fn parse_values_from_json_value(jv: serde_json::Value) -> Result<Vec<Value>> {
|
|
898
|
+
let items: Vec<FastValue> = serde_json::from_value(jv)?;
|
|
899
|
+
Ok(items.into_iter().map(|v| v.0).collect())
|
|
900
|
+
}
|