reifydb_engine/function/blob/
b58.rs1use reifydb_core::value::column::ColumnData;
5use reifydb_type::{OwnedFragment, value::Blob};
6
7use crate::function::{ScalarFunction, ScalarFunctionContext};
8
9pub struct BlobB58;
10
11impl BlobB58 {
12 pub fn new() -> Self {
13 Self
14 }
15}
16
17impl ScalarFunction for BlobB58 {
18 fn scalar(&self, ctx: ScalarFunctionContext) -> crate::Result<ColumnData> {
19 let columns = ctx.columns;
20 let row_count = ctx.row_count;
21 let column = columns.get(0).unwrap();
22
23 match &column.data() {
24 ColumnData::Utf8 {
25 container,
26 ..
27 } => {
28 let mut result_data = Vec::with_capacity(container.data().len());
29
30 for i in 0..row_count {
31 if container.is_defined(i) {
32 let b58_str = &container[i];
33 let blob = Blob::from_b58(OwnedFragment::internal(b58_str))?;
34 result_data.push(blob);
35 } else {
36 result_data.push(Blob::empty())
37 }
38 }
39
40 Ok(ColumnData::blob_with_bitvec(result_data, container.bitvec().clone()))
41 }
42 _ => unimplemented!("BlobB58 only supports text input"),
43 }
44 }
45}
46
47#[cfg(test)]
48mod tests {
49 use reifydb_core::value::{
50 column::{Column, Columns},
51 container::Utf8Container,
52 };
53 use reifydb_type::{Fragment, value::constraint::bytes::MaxBytes};
54
55 use super::*;
56 use crate::function::ScalarFunctionContext;
57
58 #[test]
59 fn test_blob_b58_valid_input() {
60 let function = BlobB58::new();
61
62 let b58_data = vec!["9Ajdvzr".to_string()];
64 let bitvec = vec![true];
65 let input_column = Column {
66 name: Fragment::borrowed_internal("input"),
67 data: ColumnData::Utf8 {
68 container: Utf8Container::new(b58_data, bitvec.into()),
69 max_bytes: MaxBytes::MAX,
70 },
71 };
72
73 let columns = Columns::new(vec![input_column]);
74 let ctx = ScalarFunctionContext {
75 columns: &columns,
76 row_count: 1,
77 };
78 let result = function.scalar(ctx).unwrap();
79
80 let ColumnData::Blob {
81 container,
82 ..
83 } = result
84 else {
85 panic!("Expected BLOB column data");
86 };
87 assert_eq!(container.len(), 1);
88 assert!(container.is_defined(0));
89 assert_eq!(container[0].as_bytes(), "Hello".as_bytes());
90 }
91
92 #[test]
93 fn test_blob_b58_empty_string() {
94 let function = BlobB58::new();
95
96 let b58_data = vec!["".to_string()];
97 let bitvec = vec![true];
98 let input_column = Column {
99 name: Fragment::borrowed_internal("input"),
100 data: ColumnData::Utf8 {
101 container: Utf8Container::new(b58_data, bitvec.into()),
102 max_bytes: MaxBytes::MAX,
103 },
104 };
105
106 let columns = Columns::new(vec![input_column]);
107 let ctx = ScalarFunctionContext {
108 columns: &columns,
109 row_count: 1,
110 };
111 let result = function.scalar(ctx).unwrap();
112
113 let ColumnData::Blob {
114 container,
115 ..
116 } = result
117 else {
118 panic!("Expected BLOB column data");
119 };
120 assert_eq!(container.len(), 1);
121 assert!(container.is_defined(0));
122 assert_eq!(container[0].as_bytes(), &[] as &[u8]);
123 }
124
125 #[test]
126 fn test_blob_b58_multiple_rows() {
127 let function = BlobB58::new();
128
129 let b58_data = vec!["28".to_string(), "63U".to_string(), "Pw25".to_string()];
131 let bitvec = vec![true, true, true];
132 let input_column = Column {
133 name: Fragment::borrowed_internal("input"),
134 data: ColumnData::Utf8 {
135 container: Utf8Container::new(b58_data, bitvec.into()),
136 max_bytes: MaxBytes::MAX,
137 },
138 };
139
140 let columns = Columns::new(vec![input_column]);
141 let ctx = ScalarFunctionContext {
142 columns: &columns,
143 row_count: 3,
144 };
145 let result = function.scalar(ctx).unwrap();
146
147 let ColumnData::Blob {
148 container,
149 ..
150 } = result
151 else {
152 panic!("Expected BLOB column data");
153 };
154 assert_eq!(container.len(), 3);
155 assert!(container.is_defined(0));
156 assert!(container.is_defined(1));
157 assert!(container.is_defined(2));
158
159 assert_eq!(container[0].as_bytes(), "A".as_bytes());
160 assert_eq!(container[1].as_bytes(), "BC".as_bytes());
161 assert_eq!(container[2].as_bytes(), "DEF".as_bytes());
162 }
163
164 #[test]
165 fn test_blob_b58_with_null_data() {
166 let function = BlobB58::new();
167
168 let b58_data = vec!["28".to_string(), "".to_string(), "Pw25".to_string()];
169 let bitvec = vec![true, false, true];
170 let input_column = Column {
171 name: Fragment::borrowed_internal("input"),
172 data: ColumnData::Utf8 {
173 container: Utf8Container::new(b58_data, bitvec.into()),
174 max_bytes: MaxBytes::MAX,
175 },
176 };
177
178 let columns = Columns::new(vec![input_column]);
179 let ctx = ScalarFunctionContext {
180 columns: &columns,
181 row_count: 3,
182 };
183 let result = function.scalar(ctx).unwrap();
184
185 let ColumnData::Blob {
186 container,
187 ..
188 } = result
189 else {
190 panic!("Expected BLOB column data");
191 };
192 assert_eq!(container.len(), 3);
193 assert!(container.is_defined(0));
194 assert!(!container.is_defined(1));
195 assert!(container.is_defined(2));
196
197 assert_eq!(container[0].as_bytes(), "A".as_bytes());
198 assert_eq!(container[1].as_bytes(), [].as_slice() as &[u8]);
199 assert_eq!(container[2].as_bytes(), "DEF".as_bytes());
200 }
201
202 #[test]
203 fn test_blob_b58_binary_data() {
204 let function = BlobB58::new();
205
206 let b58_data = vec!["6h8cQN".to_string()];
208 let bitvec = vec![true];
209 let input_column = Column {
210 name: Fragment::borrowed_internal("input"),
211 data: ColumnData::Utf8 {
212 container: Utf8Container::new(b58_data, bitvec.into()),
213 max_bytes: MaxBytes::MAX,
214 },
215 };
216
217 let columns = Columns::new(vec![input_column]);
218 let ctx = ScalarFunctionContext {
219 columns: &columns,
220 row_count: 1,
221 };
222 let result = function.scalar(ctx).unwrap();
223
224 let ColumnData::Blob {
225 container,
226 ..
227 } = result
228 else {
229 panic!("Expected BLOB column data");
230 };
231 assert_eq!(container.len(), 1);
232 assert!(container.is_defined(0));
233 assert_eq!(container[0].as_bytes(), &[0xde, 0xad, 0xbe, 0xef]);
234 }
235
236 #[test]
237 fn test_blob_b58_invalid_input_should_error() {
238 let function = BlobB58::new();
239
240 let b58_data = vec!["invalid0!".to_string()];
242 let bitvec = vec![true];
243 let input_column = Column {
244 name: Fragment::borrowed_internal("input"),
245 data: ColumnData::Utf8 {
246 container: Utf8Container::new(b58_data, bitvec.into()),
247 max_bytes: MaxBytes::MAX,
248 },
249 };
250
251 let columns = Columns::new(vec![input_column]);
252 let ctx = ScalarFunctionContext {
253 columns: &columns,
254 row_count: 1,
255 };
256 let result = function.scalar(ctx);
257 assert!(result.is_err(), "Expected error for invalid base58 input");
258 }
259
260 #[test]
261 fn test_blob_b58_invalid_zero_char() {
262 let function = BlobB58::new();
263
264 let b58_data = vec!["abc0def".to_string()];
266 let bitvec = vec![true];
267 let input_column = Column {
268 name: Fragment::borrowed_internal("input"),
269 data: ColumnData::Utf8 {
270 container: Utf8Container::new(b58_data, bitvec.into()),
271 max_bytes: MaxBytes::MAX,
272 },
273 };
274
275 let columns = Columns::new(vec![input_column]);
276 let ctx = ScalarFunctionContext {
277 columns: &columns,
278 row_count: 1,
279 };
280 let result = function.scalar(ctx);
281 assert!(result.is_err(), "Expected error for '0' in base58 input");
282 }
283}