1use gitql_ast::types::any::AnyType;
2use gitql_ast::types::float::FloatType;
3use gitql_ast::types::integer::IntType;
4use gitql_ast::types::optional::OptionType;
5use gitql_ast::types::text::TextType;
6use gitql_ast::types::variant::VariantType;
7use gitql_core::signature::Signature;
8use gitql_core::signature::StandardFunction;
9use gitql_core::values::integer::IntValue;
10use gitql_core::values::null::NullValue;
11use gitql_core::values::text::TextValue;
12use gitql_core::values::Value;
13
14use std::collections::HashMap;
15
16#[inline(always)]
17pub fn register_std_text_functions(map: &mut HashMap<&'static str, StandardFunction>) {
18 map.insert("bin", text_bin);
19 map.insert("lower", text_lowercase);
20 map.insert("upper", text_uppercase);
21 map.insert("reverse", text_reverse);
22 map.insert("replicate", text_replicate);
23 map.insert("space", text_space);
24 map.insert("trim", text_trim);
25 map.insert("ltrim", text_left_trim);
26 map.insert("rtrim", text_right_trim);
27 map.insert("len", text_len);
28 map.insert("ascii", text_ascii);
29 map.insert("left", text_left);
30 map.insert("datalength", text_datalength);
31 map.insert("char", text_char);
32 map.insert("nchar", text_char);
33 map.insert("charindex", text_charindex);
34 map.insert("replace", text_replace);
35 map.insert("substring", text_substring);
36 map.insert("stuff", text_stuff);
37 map.insert("right", text_right);
38 map.insert("translate", text_translate);
39 map.insert("soundex", text_soundex);
40 map.insert("concat", text_concat);
41 map.insert("concat_ws", text_concat_ws);
42 map.insert("unicode", text_unicode);
43 map.insert("strcmp", text_strcmp);
44 map.insert("quotename", text_quotename);
45 map.insert("str", text_str);
46 map.insert("to_hex", text_to_hex);
47}
48
49#[inline(always)]
50pub fn register_std_text_function_signatures(map: &mut HashMap<&'static str, Signature>) {
51 map.insert(
52 "bin",
53 Signature {
54 parameters: vec![Box::new(IntType)],
55 return_type: Box::new(TextType),
56 },
57 );
58 map.insert(
59 "lower",
60 Signature {
61 parameters: vec![Box::new(TextType)],
62 return_type: Box::new(TextType),
63 },
64 );
65 map.insert(
66 "upper",
67 Signature {
68 parameters: vec![Box::new(TextType)],
69 return_type: Box::new(TextType),
70 },
71 );
72 map.insert(
73 "reverse",
74 Signature {
75 parameters: vec![Box::new(TextType)],
76 return_type: Box::new(TextType),
77 },
78 );
79 map.insert(
80 "replicate",
81 Signature {
82 parameters: vec![Box::new(TextType), Box::new(IntType)],
83 return_type: Box::new(TextType),
84 },
85 );
86 map.insert(
87 "space",
88 Signature {
89 parameters: vec![Box::new(IntType)],
90 return_type: Box::new(TextType),
91 },
92 );
93 map.insert(
94 "trim",
95 Signature {
96 parameters: vec![Box::new(TextType)],
97 return_type: Box::new(TextType),
98 },
99 );
100 map.insert(
101 "ltrim",
102 Signature {
103 parameters: vec![Box::new(TextType)],
104 return_type: Box::new(TextType),
105 },
106 );
107 map.insert(
108 "rtrim",
109 Signature {
110 parameters: vec![Box::new(TextType)],
111 return_type: Box::new(TextType),
112 },
113 );
114 map.insert(
115 "len",
116 Signature {
117 parameters: vec![Box::new(TextType)],
118 return_type: Box::new(IntType),
119 },
120 );
121 map.insert(
122 "ascii",
123 Signature {
124 parameters: vec![Box::new(TextType)],
125 return_type: Box::new(IntType),
126 },
127 );
128 map.insert(
129 "left",
130 Signature {
131 parameters: vec![Box::new(TextType), Box::new(IntType)],
132 return_type: Box::new(TextType),
133 },
134 );
135 map.insert(
136 "datalength",
137 Signature {
138 parameters: vec![Box::new(TextType)],
139 return_type: Box::new(IntType),
140 },
141 );
142 map.insert(
143 "char",
144 Signature {
145 parameters: vec![Box::new(IntType)],
146 return_type: Box::new(TextType),
147 },
148 );
149 map.insert(
150 "nchar",
151 Signature {
152 parameters: vec![Box::new(IntType)],
153 return_type: Box::new(TextType),
154 },
155 );
156 map.insert(
157 "charindex",
158 Signature {
159 parameters: vec![Box::new(TextType), Box::new(TextType)],
160 return_type: Box::new(IntType),
161 },
162 );
163 map.insert(
164 "replace",
165 Signature {
166 parameters: vec![Box::new(TextType), Box::new(TextType), Box::new(TextType)],
167 return_type: Box::new(TextType),
168 },
169 );
170 map.insert(
171 "substring",
172 Signature {
173 parameters: vec![Box::new(TextType), Box::new(IntType), Box::new(IntType)],
174 return_type: Box::new(TextType),
175 },
176 );
177 map.insert(
178 "stuff",
179 Signature {
180 parameters: vec![
181 Box::new(TextType),
182 Box::new(IntType),
183 Box::new(IntType),
184 Box::new(TextType),
185 ],
186 return_type: Box::new(TextType),
187 },
188 );
189 map.insert(
190 "right",
191 Signature {
192 parameters: vec![Box::new(TextType), Box::new(IntType)],
193 return_type: Box::new(TextType),
194 },
195 );
196 map.insert(
197 "translate",
198 Signature {
199 parameters: vec![Box::new(TextType), Box::new(TextType), Box::new(TextType)],
200 return_type: Box::new(TextType),
201 },
202 );
203 map.insert(
204 "soundex",
205 Signature {
206 parameters: vec![Box::new(TextType)],
207 return_type: Box::new(TextType),
208 },
209 );
210 map.insert(
211 "concat",
212 Signature {
213 parameters: vec![
214 Box::new(AnyType),
215 Box::new(AnyType),
216 Box::new(VariantType {
217 variants: vec![Box::new(AnyType)],
218 }),
219 ],
220 return_type: Box::new(TextType),
221 },
222 );
223 map.insert(
224 "concat_ws",
225 Signature {
226 parameters: vec![
227 Box::new(TextType),
228 Box::new(AnyType),
229 Box::new(AnyType),
230 Box::new(VariantType {
231 variants: vec![Box::new(AnyType)],
232 }),
233 ],
234 return_type: Box::new(TextType),
235 },
236 );
237 map.insert(
238 "unicode",
239 Signature {
240 parameters: vec![Box::new(TextType)],
241 return_type: Box::new(IntType),
242 },
243 );
244 map.insert(
245 "strcmp",
246 Signature {
247 parameters: vec![Box::new(TextType), Box::new(TextType)],
248 return_type: Box::new(IntType),
249 },
250 );
251 map.insert(
252 "quotename",
253 Signature {
254 parameters: vec![
255 Box::new(TextType),
256 Box::new(OptionType {
257 base: Some(Box::new(TextType)),
258 }),
259 ],
260 return_type: Box::new(TextType),
261 },
262 );
263 map.insert(
264 "str",
265 Signature {
266 parameters: vec![
267 Box::new(VariantType {
268 variants: vec![Box::new(IntType), Box::new(FloatType)],
269 }),
270 Box::new(OptionType {
271 base: Some(Box::new(IntType)),
272 }),
273 Box::new(OptionType {
274 base: Some(Box::new(IntType)),
275 }),
276 ],
277 return_type: Box::new(TextType),
278 },
279 );
280 map.insert(
281 "text_to_hex",
282 Signature {
283 parameters: vec![Box::new(IntType)],
284 return_type: Box::new(TextType),
285 },
286 );
287}
288
289pub fn text_bin(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
290 let number = inputs[0].as_int().unwrap();
291 Box::new(TextValue::new(format!("{number:b}")))
292}
293
294pub fn text_lowercase(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
295 Box::new(TextValue::new(inputs[0].as_text().unwrap().to_lowercase()))
296}
297
298pub fn text_uppercase(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
299 Box::new(TextValue::new(inputs[0].as_text().unwrap().to_uppercase()))
300}
301
302pub fn text_reverse(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
303 Box::new(TextValue::new(
304 inputs[0]
305 .as_text()
306 .unwrap()
307 .chars()
308 .rev()
309 .collect::<String>(),
310 ))
311}
312
313pub fn text_replicate(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
314 let str = inputs[0].as_text().unwrap();
315 let count = inputs[1].as_int().unwrap() as usize;
316 Box::new(TextValue::new(str.repeat(count)))
317}
318
319pub fn text_space(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
320 let n = inputs[0].as_int().unwrap() as usize;
321 Box::new(TextValue::new(" ".repeat(n)))
322}
323
324pub fn text_trim(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
325 Box::new(TextValue::new(
326 inputs[0].as_text().unwrap().trim().to_string(),
327 ))
328}
329
330pub fn text_left_trim(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
331 Box::new(TextValue::new(
332 inputs[0].as_text().unwrap().trim_start().to_string(),
333 ))
334}
335
336pub fn text_right_trim(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
337 Box::new(TextValue::new(
338 inputs[0].as_text().unwrap().trim_end().to_string(),
339 ))
340}
341
342pub fn text_len(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
343 Box::new(IntValue::new(inputs[0].as_text().unwrap().len() as i64))
344}
345
346pub fn text_ascii(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
347 let text = inputs[0].as_text().unwrap();
348 let value = if text.is_empty() {
349 0
350 } else {
351 text.chars().next().unwrap() as i64
352 };
353 Box::new(IntValue::new(value))
354}
355
356pub fn text_left(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
357 let text = inputs[0].as_text().unwrap();
358 if text.is_empty() {
359 return Box::new(TextValue::empty());
360 }
361
362 let number_of_chars = inputs[1].as_int().unwrap();
363 if number_of_chars > text.len() as i64 {
364 return Box::new(TextValue::new(text));
365 }
366
367 let substring = text
368 .chars()
369 .take(number_of_chars as usize)
370 .collect::<String>();
371 Box::new(TextValue { value: substring })
372}
373
374pub fn text_datalength(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
375 let text = inputs[0].as_text().unwrap();
376 Box::new(IntValue::new(text.len() as i64))
377}
378
379pub fn text_char(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
380 let code = inputs[0].as_int().unwrap() as u32;
381 if let Some(character) = char::from_u32(code) {
382 Box::new(TextValue::new(character.to_string()))
383 } else {
384 Box::new(TextValue::empty())
385 }
386}
387
388pub fn text_charindex(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
389 let substr = inputs[0].as_text().unwrap();
390 let input = inputs[1].as_text().unwrap();
391 if let Some(index) = input.to_lowercase().find(&substr.to_lowercase()) {
392 Box::new(IntValue::new(index as i64 + 1))
393 } else {
394 Box::new(IntValue::new_zero())
395 }
396}
397
398pub fn text_replace(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
399 let text = inputs[0].as_text().unwrap();
400 let old_string = inputs[1].as_text().unwrap();
401 let new_string = inputs[2].as_text().unwrap();
402
403 let mut result = String::new();
404 let mut end = 0;
405 for (begin, matched_part) in text
406 .to_lowercase()
407 .match_indices(&old_string.to_lowercase())
408 {
409 result.push_str(text.get(end..begin).unwrap());
410 result.push_str(&new_string);
411 end = begin + matched_part.len();
412 }
413
414 result.push_str(text.get(end..text.len()).unwrap());
415 Box::new(TextValue::new(result))
416}
417
418pub fn text_substring(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
419 let text = inputs[0].as_text().unwrap();
420 let start = inputs[1].as_int().unwrap() as usize - 1;
423 let length = inputs[2].as_int().unwrap();
424
425 if start > text.len() || length > text.len() as i64 {
426 return Box::new(TextValue::new(text));
427 }
428 if length < 0 {
429 return Box::new(TextValue::empty());
430 }
431
432 let chars: Vec<char> = text.chars().collect();
434 let slice = &chars[start..(start + length as usize)];
435 Box::new(TextValue::new(slice.iter().collect()))
436}
437
438pub fn text_stuff(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
439 let text = inputs[0].as_text().unwrap();
440 let start = (inputs[1].as_int().unwrap() - 1) as usize;
441 let length = inputs[2].as_int().unwrap() as usize;
442
443 if text.is_empty() || start > text.len() || length > text.len() {
444 return Box::new(TextValue::new(text));
445 }
446
447 let mut text = text.chars().collect::<Vec<_>>();
448 let new_string = inputs[3].as_text().unwrap();
449 let new_string = new_string.chars().collect::<Vec<_>>();
450 text.splice(start..(start + length), new_string);
451 Box::new(TextValue::new(text.into_iter().collect()))
452}
453
454pub fn text_right(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
455 let text = inputs[0].as_text().unwrap();
456 if text.is_empty() {
457 return Box::new(TextValue::empty());
458 }
459
460 let number_of_chars = inputs[1].as_int().unwrap() as usize;
461 if number_of_chars > text.len() {
462 return Box::new(TextValue::new(text));
463 }
464
465 let text = text.as_str();
466 Box::new(TextValue::new(
467 text[text.len() - number_of_chars..text.len()].to_string(),
468 ))
469}
470
471pub fn text_translate(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
472 let mut text = inputs[0].as_text().unwrap();
473 let characters = inputs[1].as_text().unwrap();
474 let translations = inputs[2].as_text().unwrap();
475
476 if translations.len() != characters.len() {
477 return Box::new(TextValue::empty());
478 }
479
480 let translations = translations.chars().collect::<Vec<_>>();
481 for (idx, letter) in characters.char_indices() {
482 text = text.replace(letter, &char::to_string(&translations[idx]));
483 }
484
485 Box::new(TextValue::new(text))
486}
487
488pub fn text_unicode(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
489 let value = if let Some(c) = inputs[0].as_text().unwrap().chars().next() {
490 (c as u32).into()
491 } else {
492 0
493 };
494 Box::new(IntValue::new(value))
495}
496
497pub fn text_soundex(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
498 let text = inputs[0].as_text().unwrap();
499 if text.is_empty() {
500 return Box::new(TextValue::empty());
501 }
502
503 let mut result = String::from(text.chars().next().unwrap());
504 for (idx, letter) in text.char_indices() {
505 if idx != 0 {
506 let letter = letter.to_ascii_uppercase();
507 if !matches!(letter, 'A' | 'E' | 'I' | 'O' | 'U' | 'H' | 'W' | 'Y') {
508 let int = match letter {
509 'B' | 'F' | 'P' | 'V' => 1,
510 'C' | 'G' | 'J' | 'K' | 'Q' | 'S' | 'X' | 'Z' => 2,
511 'D' | 'T' => 3,
512 'L' => 4,
513 'M' | 'N' => 5,
514 'R' => 6,
515 _ => 0,
516 };
517 result.push_str(&int.to_string());
518
519 if result.len() == 4 {
520 return Box::new(TextValue::new(result));
521 }
522 }
523 }
524 }
525
526 if result.len() < 4 {
527 let diff = 4 - result.len();
528 for _i in 0..diff {
529 result.push_str(&0.to_string());
530 }
531 }
532
533 Box::new(TextValue::new(result))
534}
535
536pub fn text_concat(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
537 let text: Vec<String> = inputs.iter().map(|v| v.to_string()).collect();
538 Box::new(TextValue::new(text.concat()))
539}
540
541pub fn text_concat_ws(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
542 let separator = inputs[0].as_text().unwrap();
543 let text: Vec<String> = inputs.iter().skip(1).map(|v| v.to_string()).collect();
544 let value = text.join(&separator);
545 Box::new(TextValue::new(value))
546}
547
548pub fn text_strcmp(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
549 let value = match inputs[0].as_text().cmp(&inputs[1].as_text()) {
550 std::cmp::Ordering::Less => 1,
551 std::cmp::Ordering::Equal => 2,
552 std::cmp::Ordering::Greater => 0,
553 };
554 Box::new(IntValue::new(value))
555}
556
557pub fn text_quotename(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
558 let str = inputs[0].as_text().unwrap();
559 let quote = inputs
560 .get(1)
561 .map(|v| v.as_text().unwrap())
562 .map(|str| str.chars().collect())
563 .unwrap_or_else(|| vec!['[', ']']);
564
565 match quote.as_slice() {
566 [single] => Box::new(TextValue {
567 value: format!("{single}{str}{single}"),
568 }),
569 [start, end] => Box::new(TextValue {
570 value: format!("{start}{str}{end}"),
571 }),
572 _ => Box::new(NullValue),
573 }
574}
575
576pub fn text_str(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
577 let value = &inputs[0];
578 let length = if inputs.len() == 3 {
579 inputs[1].as_int().unwrap()
580 } else {
581 10
582 };
583
584 let decimals = if inputs.len() == 3 {
585 inputs[2].as_int().unwrap()
586 } else {
587 0
588 };
589
590 if value.data_type().is_int() {
591 let int_value = value.as_int().unwrap();
592 let number_string = format!("{:.dec$}", int_value, dec = decimals as usize);
593 if length > 0 {
594 if (length as usize) < number_string.len() {
595 return Box::new(TextValue::new(number_string[..length as usize].to_owned()));
596 }
597
598 return Box::new(TextValue::new(format!(
599 "{:<len$}",
600 number_string,
601 len = length as usize
602 )));
603 }
604
605 return Box::new(TextValue::new(number_string.clone()));
606 }
607
608 let float_value = value.as_float().unwrap();
609 let number_string = format!("{:.dec$}", float_value, dec = decimals as usize);
610 if length > 0 {
611 if (length as usize) < number_string.len() {
612 return Box::new(TextValue::new(number_string[..length as usize].to_owned()));
613 }
614
615 return Box::new(TextValue::new(format!(
616 "{:<len$}",
617 number_string,
618 len = length as usize
619 )));
620 }
621
622 Box::new(TextValue::new(number_string.clone()))
623}
624
625pub fn text_to_hex(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
626 let number = inputs[0].as_int().unwrap();
627 let value = format!("0x{}", number);
628 Box::new(TextValue::new(value))
629}