gluesql_core/data/
string_ext.rs1use {
2 crate::result::Result,
3 regex::{Regex, RegexBuilder},
4 serde::Serialize,
5 thiserror::Error,
6};
7
8#[derive(Error, Serialize, Debug, PartialEq, Eq)]
9pub enum StringExtError {
10 #[error("unreachable literal unary operation")]
11 UnreachablePatternParsing,
12 #[error("invalid regular expression {pattern:?}: {error}")]
13 InvalidRegexPattern { pattern: String, error: String },
14}
15
16pub trait StringExt {
17 fn like(&self, pattern: &str, case_sensitive: bool) -> Result<bool>;
18 fn regex(&self, pattern: &str, case_sensitive: bool) -> Result<bool>;
19}
20
21impl StringExt for str {
22 fn like(&self, pattern: &str, case_sensitive: bool) -> Result<bool> {
23 let (match_string, match_pattern) = if case_sensitive {
24 (self.to_owned(), pattern.to_owned())
25 } else {
26 let lowercase_string = self.to_lowercase();
27 let lowercase_pattern = pattern.to_lowercase();
28
29 (lowercase_string, lowercase_pattern)
30 };
31
32 Ok(Regex::new(&format!(
33 "^{}$",
34 regex::escape(match_pattern.as_str())
35 .replace('%', ".*")
36 .replace('_', ".")
37 ))
38 .map_err(|_| StringExtError::UnreachablePatternParsing)?
39 .is_match(match_string.as_str()))
40 }
41
42 fn regex(&self, pattern: &str, case_sensitive: bool) -> Result<bool> {
43 Ok(RegexBuilder::new(pattern)
44 .case_insensitive(!case_sensitive)
45 .build()
46 .map_err(|error| StringExtError::InvalidRegexPattern {
47 pattern: pattern.to_owned(),
48 error: error.to_string(),
49 })
50 .map(|regex| regex.is_match(self))?)
51 }
52}
53
54#[cfg(test)]
55mod tests {
56 use {
57 super::{StringExt, StringExtError},
58 crate::result::Error,
59 };
60
61 #[test]
62 fn regex() {
63 assert_eq!("Hello".regex("ell", true), Ok(true));
64 assert_eq!("Hello".regex("^hello$", true), Ok(false));
65 assert_eq!("Hello".regex("^hello$", false), Ok(true));
66 assert!(matches!(
67 "Hello".regex("[", true),
68 Err(Error::StringExt(StringExtError::InvalidRegexPattern { pattern, .. })) if pattern == "["
69 ));
70 assert!(matches!(
71 "Hello".regex("(?i)[", true),
72 Err(Error::StringExt(StringExtError::InvalidRegexPattern { pattern, .. })) if pattern == "(?i)["
73 ));
74 assert!(matches!(
75 "Hello".regex("[", false),
76 Err(Error::StringExt(StringExtError::InvalidRegexPattern { pattern, .. })) if pattern == "["
77 ));
78
79 assert!(matches!(
81 "Hello".regex("[", false),
82 Err(Error::StringExt(StringExtError::InvalidRegexPattern { error, .. }))
83 if !error.contains("(?i)")
84 ));
85 }
86}