Skip to main content

reifydb_function/blob/
b64.rs

1// SPDX-License-Identifier: AGPL-3.0-or-later
2// Copyright (c) 2025 ReifyDB
3
4use reifydb_core::value::column::data::ColumnData;
5use reifydb_type::{
6	fragment::Fragment,
7	value::{blob::Blob, r#type::Type},
8};
9
10use crate::{
11	ScalarFunction, ScalarFunctionContext,
12	error::{ScalarFunctionError, ScalarFunctionResult},
13	propagate_options,
14};
15
16pub struct BlobB64;
17
18impl BlobB64 {
19	pub fn new() -> Self {
20		Self
21	}
22}
23
24impl ScalarFunction for BlobB64 {
25	fn scalar(&self, ctx: ScalarFunctionContext) -> ScalarFunctionResult<ColumnData> {
26		if let Some(result) = propagate_options(self, &ctx) {
27			return result;
28		}
29
30		let columns = ctx.columns;
31		let row_count = ctx.row_count;
32
33		// Validate exactly 1 argument
34		if columns.len() != 1 {
35			return Err(ScalarFunctionError::ArityMismatch {
36				function: ctx.fragment.clone(),
37				expected: 1,
38				actual: columns.len(),
39			});
40		}
41
42		let column = columns.get(0).unwrap();
43
44		match &column.data() {
45			ColumnData::Utf8 {
46				container,
47				..
48			} => {
49				let mut result_data = Vec::with_capacity(container.data().len());
50				let mut result_bitvec = Vec::with_capacity(row_count);
51
52				for i in 0..row_count {
53					if container.is_defined(i) {
54						let b64_str = &container[i];
55						let blob = Blob::from_b64(Fragment::internal(b64_str))?;
56						result_data.push(blob);
57						result_bitvec.push(true);
58					} else {
59						result_data.push(Blob::empty());
60						result_bitvec.push(false);
61					}
62				}
63
64				Ok(ColumnData::blob_with_bitvec(result_data, result_bitvec))
65			}
66			other => Err(ScalarFunctionError::InvalidArgumentType {
67				function: ctx.fragment.clone(),
68				argument_index: 0,
69				expected: vec![Type::Utf8],
70				actual: other.get_type(),
71			}),
72		}
73	}
74
75	fn return_type(&self, _input_types: &[Type]) -> Type {
76		Type::Blob
77	}
78}