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 {
292 value: format!("{number:b}"),
293 })
294}
295
296pub fn text_lowercase(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
297 Box::new(TextValue {
298 value: inputs[0].as_text().unwrap().to_lowercase(),
299 })
300}
301
302pub fn text_uppercase(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
303 Box::new(TextValue {
304 value: inputs[0].as_text().unwrap().to_uppercase(),
305 })
306}
307
308pub fn text_reverse(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
309 Box::new(TextValue {
310 value: inputs[0]
311 .as_text()
312 .unwrap()
313 .chars()
314 .rev()
315 .collect::<String>(),
316 })
317}
318
319pub fn text_replicate(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
320 let str = inputs[0].as_text().unwrap();
321 let count = inputs[1].as_int().unwrap() as usize;
322 Box::new(TextValue {
323 value: str.repeat(count),
324 })
325}
326
327pub fn text_space(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
328 let n = inputs[0].as_int().unwrap() as usize;
329 Box::new(TextValue {
330 value: " ".repeat(n),
331 })
332}
333
334pub fn text_trim(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
335 Box::new(TextValue {
336 value: inputs[0].as_text().unwrap().trim().to_string(),
337 })
338}
339
340pub fn text_left_trim(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
341 Box::new(TextValue {
342 value: inputs[0].as_text().unwrap().trim_start().to_string(),
343 })
344}
345
346pub fn text_right_trim(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
347 Box::new(TextValue {
348 value: inputs[0].as_text().unwrap().trim_end().to_string(),
349 })
350}
351
352pub fn text_len(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
353 Box::new(IntValue {
354 value: inputs[0].as_text().unwrap().len() as i64,
355 })
356}
357
358pub fn text_ascii(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
359 let text = inputs[0].as_text().unwrap();
360 let value = if text.is_empty() {
361 0
362 } else {
363 text.chars().next().unwrap() as i64
364 };
365
366 Box::new(IntValue { value })
367}
368
369pub fn text_left(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
370 let text = inputs[0].as_text().unwrap();
371 if text.is_empty() {
372 return Box::new(TextValue {
373 value: "".to_string(),
374 });
375 }
376
377 let number_of_chars = inputs[1].as_int().unwrap();
378 if number_of_chars > text.len() as i64 {
379 return Box::new(TextValue { value: text });
380 }
381
382 let substring = text
383 .chars()
384 .take(number_of_chars as usize)
385 .collect::<String>();
386 Box::new(TextValue { value: substring })
387}
388
389pub fn text_datalength(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
390 let text = inputs[0].as_text().unwrap();
391 Box::new(IntValue::new(text.len() as i64))
392}
393
394pub fn text_char(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
395 let code = inputs[0].as_int().unwrap() as u32;
396 let value = if let Some(character) = char::from_u32(code) {
397 character.to_string()
398 } else {
399 "".to_string()
400 };
401 Box::new(TextValue { value })
402}
403
404pub fn text_charindex(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
405 let substr = inputs[0].as_text().unwrap();
406 let input = inputs[1].as_text().unwrap();
407
408 let value = if let Some(index) = input.to_lowercase().find(&substr.to_lowercase()) {
409 index as i64 + 1
410 } else {
411 0
412 };
413
414 Box::new(IntValue { value })
415}
416
417pub fn text_replace(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
418 let text = inputs[0].as_text().unwrap();
419 let old_string = inputs[1].as_text().unwrap();
420 let new_string = inputs[2].as_text().unwrap();
421
422 let mut result = String::new();
423 let mut end = 0;
424 for (begin, matched_part) in text
425 .to_lowercase()
426 .match_indices(&old_string.to_lowercase())
427 {
428 result.push_str(text.get(end..begin).unwrap());
429 result.push_str(&new_string);
430 end = begin + matched_part.len();
431 }
432
433 result.push_str(text.get(end..text.len()).unwrap());
434 Box::new(TextValue { value: result })
435}
436
437pub fn text_substring(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
438 let text = inputs[0].as_text().unwrap();
439 let start = inputs[1].as_int().unwrap() as usize - 1;
442 let length = inputs[2].as_int().unwrap();
443
444 if start > text.len() || length > text.len() as i64 {
445 return Box::new(TextValue { value: text });
446 }
447 if length < 0 {
448 return Box::new(TextValue {
449 value: "".to_string(),
450 });
451 }
452
453 let chars: Vec<char> = text.chars().collect();
455 let slice = &chars[start..(start + length as usize)];
456 Box::new(TextValue {
457 value: slice.iter().collect(),
458 })
459}
460
461pub fn text_stuff(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
462 let text = inputs[0].as_text().unwrap();
463 let start = (inputs[1].as_int().unwrap() - 1) as usize;
464 let length = inputs[2].as_int().unwrap() as usize;
465 let new_string = inputs[3].as_text().unwrap();
466
467 if text.is_empty() {
468 return Box::new(TextValue { value: text });
469 }
470
471 if start > text.len() || length > text.len() {
472 return Box::new(TextValue { value: text });
473 }
474
475 let mut text = text.chars().collect::<Vec<_>>();
476 let new_string = new_string.chars().collect::<Vec<_>>();
477 text.splice(start..(start + length), new_string);
478 Box::new(TextValue {
479 value: text.into_iter().collect(),
480 })
481}
482
483pub fn text_right(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
484 let text = inputs[0].as_text().unwrap();
485 if text.is_empty() {
486 return Box::new(TextValue {
487 value: "".to_string(),
488 });
489 }
490
491 let number_of_chars = inputs[1].as_int().unwrap() as usize;
492 if number_of_chars > text.len() {
493 return Box::new(TextValue { value: text });
494 }
495
496 let text = text.as_str();
497 Box::new(TextValue {
498 value: text[text.len() - number_of_chars..text.len()].to_string(),
499 })
500}
501
502pub fn text_translate(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
503 let mut text = inputs[0].as_text().unwrap();
504 let characters = inputs[1].as_text().unwrap();
505 let translations = inputs[2].as_text().unwrap();
506
507 if translations.len() != characters.len() {
508 return Box::new(TextValue {
509 value: "".to_string(),
510 });
511 }
512
513 let translations = translations.chars().collect::<Vec<_>>();
514 for (idx, letter) in characters.char_indices() {
515 text = text.replace(letter, &char::to_string(&translations[idx]));
516 }
517
518 Box::new(TextValue { value: text })
519}
520
521pub fn text_unicode(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
522 let value = if let Some(c) = inputs[0].as_text().unwrap().chars().next() {
523 (c as u32).into()
524 } else {
525 0
526 };
527
528 Box::new(IntValue { value })
529}
530
531pub fn text_soundex(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
532 let text = inputs[0].as_text().unwrap();
533 if text.is_empty() {
534 return Box::new(TextValue {
535 value: "".to_string(),
536 });
537 }
538
539 let mut result = String::from(text.chars().next().unwrap());
540
541 for (idx, letter) in text.char_indices() {
542 if idx != 0 {
543 let letter = letter.to_ascii_uppercase();
544 if !matches!(letter, 'A' | 'E' | 'I' | 'O' | 'U' | 'H' | 'W' | 'Y') {
545 let int = match letter {
546 'B' | 'F' | 'P' | 'V' => 1,
547 'C' | 'G' | 'J' | 'K' | 'Q' | 'S' | 'X' | 'Z' => 2,
548 'D' | 'T' => 3,
549 'L' => 4,
550 'M' | 'N' => 5,
551 'R' => 6,
552 _ => 0,
553 };
554 result.push_str(&int.to_string());
555
556 if result.len() == 4 {
557 return Box::new(TextValue { value: result });
558 }
559 }
560 }
561 }
562
563 if result.len() < 4 {
564 let diff = 4 - result.len();
565 for _i in 0..diff {
566 result.push_str(&0.to_string());
567 }
568 }
569
570 Box::new(TextValue { value: result })
571}
572
573pub fn text_concat(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
574 let text: Vec<String> = inputs.iter().map(|v| v.to_string()).collect();
575 Box::new(TextValue {
576 value: text.concat(),
577 })
578}
579
580pub fn text_concat_ws(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
581 let separator = inputs[0].as_text().unwrap();
582 let text: Vec<String> = inputs.iter().skip(1).map(|v| v.to_string()).collect();
583 let value = text.join(&separator);
584 Box::new(TextValue { value })
585}
586
587pub fn text_strcmp(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
588 let value = match inputs[0].as_text().cmp(&inputs[1].as_text()) {
589 std::cmp::Ordering::Less => 1,
590 std::cmp::Ordering::Equal => 2,
591 std::cmp::Ordering::Greater => 0,
592 };
593 Box::new(IntValue { value })
594}
595
596pub fn text_quotename(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
597 let str = inputs[0].as_text().unwrap();
598 let quote = inputs
599 .get(1)
600 .map(|v| v.as_text().unwrap())
601 .map(|str| str.chars().collect())
602 .unwrap_or_else(|| vec!['[', ']']);
603
604 match quote.as_slice() {
605 [single] => Box::new(TextValue {
606 value: format!("{single}{str}{single}"),
607 }),
608 [start, end] => Box::new(TextValue {
609 value: format!("{start}{str}{end}"),
610 }),
611 _ => Box::new(NullValue),
612 }
613}
614
615pub fn text_str(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
616 let value = &inputs[0];
617 let length = if inputs.len() == 3 {
618 inputs[1].as_int().unwrap()
619 } else {
620 10
621 };
622
623 let decimals = if inputs.len() == 3 {
624 inputs[2].as_int().unwrap()
625 } else {
626 0
627 };
628
629 if value.data_type().is_int() {
630 let int_value = value.as_int().unwrap();
631 let number_string = format!("{:.dec$}", int_value, dec = decimals as usize);
632 if length > 0 {
633 if (length as usize) < number_string.len() {
634 return Box::new(TextValue {
635 value: number_string[..length as usize].to_owned(),
636 });
637 } else {
638 return Box::new(TextValue {
639 value: format!("{:<len$}", number_string, len = length as usize),
640 });
641 }
642 }
643
644 return Box::new(TextValue {
645 value: number_string.clone(),
646 });
647 }
648
649 let float_value = value.as_float().unwrap();
650 let number_string = format!("{:.dec$}", float_value, dec = decimals as usize);
651 if length > 0 {
652 if (length as usize) < number_string.len() {
653 return Box::new(TextValue {
654 value: number_string[..length as usize].to_owned(),
655 });
656 } else {
657 return Box::new(TextValue {
658 value: format!("{:<len$}", number_string, len = length as usize),
659 });
660 }
661 }
662
663 Box::new(TextValue {
664 value: number_string.clone(),
665 })
666}
667
668pub fn text_to_hex(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
669 let number = inputs[0].as_int().unwrap();
670 let value = format!("0x{}", number);
671 Box::new(TextValue { value })
672}