Skip to main content

reifydb_codec/json/
to.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 ReifyDB
3
4use reifydb_value::{
5	util::hex::encode,
6	value::{Value, column_view::ColumnView, diff_type::DiffType, frame::frame::Frame, value_type::ValueType},
7};
8use serde::ser::Error as _;
9use serde_json::{Error, Value as JsonValue, to_string};
10
11use crate::{
12	json::{
13		none_marker,
14		types::{ResponseColumn, ResponseFrame},
15		wire_type::WireValueType,
16	},
17	tag::peel_options,
18};
19
20pub fn value_to_json(value: &Value, declared: &ValueType) -> JsonValue {
21	let depth = peel_options(declared).1;
22	match value {
23		Value::None {
24			inner,
25		} => JsonValue::String(none_marker(depth.saturating_sub(peel_options(inner).1 + 1))),
26		Value::Blob(b) => JsonValue::String(b.to_hex()),
27		Value::Digest(digest) => JsonValue::String(format!("0x{}", encode(&digest.encode()))),
28		Value::List(items) => {
29			let elem_ty = match peel_options(declared).0 {
30				ValueType::List(inner) => inner.as_ref(),
31				_ => &ValueType::Any,
32			};
33			JsonValue::Array(items.iter().map(|item| value_to_json(item, elem_ty)).collect())
34		}
35		Value::Record(fields) => {
36			let field_types: &[(String, ValueType)] = match peel_options(declared).0 {
37				ValueType::Record(f) => f.as_slice(),
38				_ => &[],
39			};
40			JsonValue::Object(
41				fields.iter()
42					.map(|(name, item)| {
43						let field_ty = field_types
44							.iter()
45							.find(|(n, _)| n == name)
46							.map(|(_, t)| t)
47							.unwrap_or(&ValueType::Any);
48						(name.clone(), value_to_json(item, field_ty))
49					})
50					.collect(),
51			)
52		}
53		_ => JsonValue::String(value.to_string()),
54	}
55}
56
57pub fn convert_frames(frames: &[Frame]) -> Result<Vec<ResponseFrame>, Error> {
58	let mut result = Vec::new();
59
60	for frame in frames {
61		let mut columns = Vec::new();
62
63		let schema = frame.batch.schema_ref();
64		for (field, array) in schema.fields().iter().zip(frame.batch.columns()) {
65			let view = ColumnView::try_from((array, field.as_ref())).map_err(Error::custom)?;
66			let column_type = view.get_type();
67			let column_data: Vec<JsonValue> =
68				view.iter().map(|value| value_to_json(&value, &column_type)).collect();
69
70			columns.push(ResponseColumn {
71				name: field.name().clone(),
72				r#type: WireValueType(column_type),
73				payload: column_data,
74			});
75		}
76
77		result.push(ResponseFrame {
78			op: frame.op.map(DiffType::as_u8),
79			row_numbers: Vec::new(),
80			created_at: Vec::new(),
81			updated_at: Vec::new(),
82			time: Vec::new(),
83			columns,
84		});
85	}
86
87	Ok(result)
88}
89
90pub fn frames_to_json(frames: &[Frame]) -> Result<String, Error> {
91	let response_frames = convert_frames(frames)?;
92	to_string(&response_frames)
93}