gitql_std/regex/
mod.rs

1use gitql_ast::types::boolean::BoolType;
2use gitql_ast::types::integer::IntType;
3use gitql_ast::types::text::TextType;
4use gitql_core::signature::Signature;
5use gitql_core::signature::StandardFunction;
6use gitql_core::values::boolean::BoolValue;
7use gitql_core::values::integer::IntValue;
8use gitql_core::values::text::TextValue;
9use gitql_core::values::Value;
10
11use std::collections::HashMap;
12
13use regex::Regex;
14
15#[inline(always)]
16pub fn register_std_regex_functions(map: &mut HashMap<&'static str, StandardFunction>) {
17    map.insert("regexp_instr", regexp_instr);
18    map.insert("regexp_like", regexp_like);
19    map.insert("regexp_replace", regexp_replace);
20    map.insert("regexp_substr", regexp_substr);
21}
22
23#[inline(always)]
24pub fn register_std_regex_function_signatures(map: &mut HashMap<&'static str, Signature>) {
25    map.insert(
26        "regexp_instr",
27        Signature {
28            parameters: vec![Box::new(TextType), Box::new(TextType)],
29            return_type: Box::new(IntType),
30        },
31    );
32    map.insert(
33        "regexp_like",
34        Signature {
35            parameters: vec![Box::new(TextType), Box::new(TextType)],
36            return_type: Box::new(BoolType),
37        },
38    );
39    map.insert(
40        "regexp_replace",
41        Signature {
42            parameters: vec![Box::new(TextType), Box::new(TextType), Box::new(TextType)],
43            return_type: Box::new(TextType),
44        },
45    );
46    map.insert(
47        "regexp_substr",
48        Signature {
49            parameters: vec![Box::new(TextType), Box::new(TextType)],
50            return_type: Box::new(TextType),
51        },
52    );
53}
54
55/// Return the position of the pattern in the input
56/// If the pattern compilation fails, it returns -1
57/// If a match is found returns the position of the match's start offset (adjusted by 1)
58pub fn regexp_instr(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
59    let input = inputs[0].as_text().unwrap();
60    let pattern = inputs[1].as_text().unwrap();
61    if let Ok(regex) = Regex::new(&pattern) {
62        if let Some(match_result) = regex.find(&input) {
63            let value = (match_result.start() + 1) as i64;
64            return Box::new(IntValue { value });
65        }
66    }
67    Box::new(IntValue { value: -1 })
68}
69
70/// Return true if a match is found, overwise return false
71pub fn regexp_like(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
72    let input = inputs[0].as_text().unwrap();
73    let pattern = inputs[1].as_text().unwrap();
74    if let Ok(regex) = Regex::new(&pattern) {
75        return Box::new(BoolValue {
76            value: regex.is_match(&input),
77        });
78    }
79    Box::new(BoolValue { value: false })
80}
81
82/// Return the input after replacing pattern with new content
83/// Or return the same input if the pattern is invalid
84pub fn regexp_replace(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
85    let input = inputs[0].as_text().unwrap();
86    let pattern = inputs[1].as_text().unwrap();
87    let replacement = inputs[2].as_text().unwrap();
88    if let Ok(regex) = Regex::new(&pattern) {
89        let value = regex.replace_all(&input, replacement).to_string();
90        return Box::new(TextValue { value });
91    }
92    Box::new(TextValue { value: input })
93}
94
95/// Return substring matching regular expression or empty string if no match found
96pub fn regexp_substr(inputs: &[Box<dyn Value>]) -> Box<dyn Value> {
97    let input = inputs[0].as_text().unwrap();
98    let pattern = inputs[1].as_text().unwrap();
99    if let Ok(regex) = Regex::new(&pattern) {
100        if let Some(mat) = regex.find(&input) {
101            return Box::new(TextValue {
102                value: mat.as_str().to_string(),
103            });
104        }
105    }
106    Box::new(TextValue {
107        value: "".to_string(),
108    })
109}