Skip to main content

reifydb_sub_server/
response.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 ReifyDB
3
4use reifydb_value::{reifydb_assertions, value::frame::frame::Frame};
5use reifydb_wire_format::{encode::encode_frames, options::EncodeOptions};
6use serde_json::{self, Map, Value as JsonValue, to_string as json_to_string};
7
8pub const CONTENT_TYPE_JSON: &str = "application/vnd.reifydb.json";
9pub const CONTENT_TYPE_FRAMES: &str = "application/vnd.reifydb.frames";
10pub const CONTENT_TYPE_RBCF: &str = "application/vnd.reifydb.rbcf";
11pub const CONTENT_TYPE_PROTO: &str = "application/vnd.reifydb.proto";
12
13pub fn encode_frames_rbcf(frames: &[Frame]) -> Result<Vec<u8>, String> {
14	encode_frames(frames, &EncodeOptions::fast()).map_err(|e| e.to_string())
15}
16
17pub struct ResolvedResponse {
18	pub content_type: String,
19	pub body: String,
20}
21
22pub fn resolve_response_json(frames: Vec<Frame>, unwrap: bool) -> Result<ResolvedResponse, String> {
23	if frames.is_empty() {
24		return Ok(json_response("[]".to_string()));
25	}
26
27	if has_body_column(&frames) {
28		let frame = frames.into_iter().next().unwrap();
29		return Ok(json_response(render_body_column(frame, unwrap)));
30	}
31
32	Ok(json_response(render_frame_rows(&frames, unwrap)))
33}
34
35#[inline]
36fn json_response(body: String) -> ResolvedResponse {
37	ResolvedResponse {
38		content_type: CONTENT_TYPE_JSON.to_string(),
39		body,
40	}
41}
42
43#[inline]
44fn has_body_column(frames: &[Frame]) -> bool {
45	frames.first().map(|f| f.columns.iter().any(|c| c.name == "body")).unwrap_or(false)
46}
47
48#[inline]
49fn render_body_column(frame: Frame, unwrap: bool) -> String {
50	let body_col_idx = frame.columns.iter().position(|c| c.name == "body").unwrap();
51	let body_col = &frame.columns[body_col_idx];
52
53	let row_count = body_col.data.len();
54	if body_col.data.is_utf8() {
55		let values: Vec<String> = (0..row_count).map(|i| body_col.data.as_string(i)).collect();
56		if unwrap || values.len() == 1 {
57			values.into_iter().next().unwrap()
58		} else {
59			format!("[{}]", values.join(", "))
60		}
61	} else {
62		let json_values: Vec<JsonValue> =
63			(0..row_count).map(|i| body_col.data.get_value(i).to_json_value()).collect();
64		reifydb_assertions! {
65			let len = json_values.len();
66			assert!(
67				len > 0,
68				"render_body_column reached the non-utf8 branch with an empty body column, but the \
69				 caller already established the frame is non-empty by detecting a body column on it; \
70				 indexing json_values[0] would panic on a row_count={len} column"
71			);
72		}
73		if unwrap {
74			json_to_string(&json_values[0]).unwrap()
75		} else {
76			json_to_string(&json_values).unwrap()
77		}
78	}
79}
80
81#[inline]
82fn render_frame_rows(frames: &[Frame], unwrap: bool) -> String {
83	let json_frames = frames_to_json_rows(frames);
84
85	if unwrap && json_frames.len() == 1 && json_frames[0].len() == 1 {
86		json_to_string(&json_frames[0][0]).unwrap()
87	} else {
88		json_to_string(&json_frames).unwrap()
89	}
90}
91
92fn frames_to_json_rows(frames: &[Frame]) -> Vec<Vec<JsonValue>> {
93	frames.iter()
94		.map(|frame| {
95			let row_count = frame.columns.first().map(|c| c.data.len()).unwrap_or(0);
96			(0..row_count)
97				.map(|i| {
98					let mut obj = Map::new();
99					for col in frame.iter() {
100						obj.insert(col.name.clone(), col.data.get_value(i).to_json_value());
101					}
102					JsonValue::Object(obj)
103				})
104				.collect()
105		})
106		.collect()
107}