Skip to main content

reifydb_routine/function/json/
object.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2025 ReifyDB
3
4use reifydb_core::value::column::{ColumnWithName, buffer::ColumnBuffer, columns::Columns};
5use reifydb_type::{
6	util::bitvec::BitVec,
7	value::{Value, r#type::Type},
8};
9
10use crate::routine::{Function, FunctionKind, Routine, RoutineInfo, context::FunctionContext, error::RoutineError};
11
12pub struct JsonObject {
13	info: RoutineInfo,
14}
15
16impl Default for JsonObject {
17	fn default() -> Self {
18		Self::new()
19	}
20}
21
22impl JsonObject {
23	pub fn new() -> Self {
24		Self {
25			info: RoutineInfo::new("json::object"),
26		}
27	}
28}
29
30impl<'a> Routine<FunctionContext<'a>> for JsonObject {
31	fn info(&self) -> &RoutineInfo {
32		&self.info
33	}
34
35	fn return_type(&self, _input_types: &[Type]) -> Type {
36		Type::Any
37	}
38
39	fn execute(&self, ctx: &mut FunctionContext<'a>, args: &Columns) -> Result<Columns, RoutineError> {
40		let mut unwrapped: Vec<_> = Vec::with_capacity(args.len());
41		let mut combined_bv: Option<BitVec> = None;
42
43		for col in args.iter() {
44			let (data, bitvec) = col.data().unwrap_option();
45			if let Some(bv) = bitvec {
46				combined_bv = Some(match combined_bv {
47					Some(existing) => existing.and(bv),
48					None => bv.clone(),
49				});
50			}
51			unwrapped.push(data);
52		}
53
54		if !unwrapped.len().is_multiple_of(2) {
55			return Err(RoutineError::FunctionExecutionFailed {
56				function: ctx.fragment.clone(),
57				reason: "json::object requires an even number of arguments (key-value pairs)"
58					.to_string(),
59			});
60		}
61
62		for i in (0..unwrapped.len()).step_by(2) {
63			let col_data = unwrapped[i];
64			match col_data {
65				ColumnBuffer::Utf8 {
66					..
67				} => {}
68				other => {
69					return Err(RoutineError::FunctionInvalidArgumentType {
70						function: ctx.fragment.clone(),
71						argument_index: i,
72						expected: vec![Type::Utf8],
73						actual: other.get_type(),
74					});
75				}
76			}
77		}
78
79		let row_count = if unwrapped.is_empty() {
80			1
81		} else {
82			unwrapped[0].len()
83		};
84		let num_pairs = unwrapped.len() / 2;
85		let mut results: Vec<Box<Value>> = Vec::with_capacity(row_count);
86
87		for row in 0..row_count {
88			let mut fields = Vec::with_capacity(num_pairs);
89			for pair in 0..num_pairs {
90				let key_data = unwrapped[pair * 2];
91				let val_data = unwrapped[pair * 2 + 1];
92
93				let key: String = key_data.get_as::<String>(row).unwrap_or_default();
94				let value = val_data.get_value(row);
95
96				fields.push((key, value));
97			}
98			results.push(Box::new(Value::Record(fields)));
99		}
100
101		let result_data = ColumnBuffer::any(results);
102		let final_data = match combined_bv {
103			Some(bv) => ColumnBuffer::Option {
104				inner: Box::new(result_data),
105				bitvec: bv,
106			},
107			None => result_data,
108		};
109
110		Ok(Columns::new(vec![ColumnWithName::new(ctx.fragment.clone(), final_data)]))
111	}
112}
113
114impl Function for JsonObject {
115	fn kinds(&self) -> &[FunctionKind] {
116		&[FunctionKind::Scalar]
117	}
118}