reifydb-codec 0.9.3

Unified encoding and decoding for all ReifyDB values, frames, rows, keys, and boundaries
Documentation
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) 2026 ReifyDB

use reifydb_value::value::{Value, diff_type::DiffType, frame::frame::Frame};
use serde_json::{Error, to_string};

use crate::{
	json::{
		none_marker,
		types::{ResponseColumn, ResponseFrame},
		wire_type::WireValueType,
	},
	tag::peel_options,
};

pub fn convert_frames(frames: &[Frame]) -> Vec<ResponseFrame> {
	let mut result = Vec::new();

	for frame in frames {
		let row_numbers: Vec<u64> = frame.row_numbers().iter().map(|rn| rn.value()).collect();
		let created_at: Vec<String> = frame.created_at().iter().map(|dt| dt.to_string()).collect();
		let updated_at: Vec<String> = frame.updated_at().iter().map(|dt| dt.to_string()).collect();
		let time: Vec<String> = frame.time().iter().map(|dt| dt.to_string()).collect();

		let mut columns = Vec::new();

		for column in frame.iter() {
			let column_type = column.data.get_type();
			let column_depth = peel_options(&column_type).1;
			let column_data: Vec<String> = column
				.data
				.iter()
				.map(|value| match value {
					Value::None {
						inner,
					} => none_marker(column_depth.saturating_sub(peel_options(&inner).1 + 1)),
					Value::Blob(b) => b.to_hex(),
					_ => value.to_string(),
				})
				.collect();

			columns.push(ResponseColumn {
				name: column.name.clone(),
				r#type: WireValueType(column_type),
				payload: column_data,
			});
		}

		result.push(ResponseFrame {
			op: frame.op.map(DiffType::as_u8),
			row_numbers,
			created_at,
			updated_at,
			time,
			columns,
		});
	}

	result
}

pub fn frames_to_json(frames: &[Frame]) -> Result<String, Error> {
	let response_frames = convert_frames(frames);
	to_string(&response_frames)
}