ddb_core/functions/
utility.rs1use super::Value;
3use crate::error::Result;
4use std::collections::hash_map::DefaultHasher;
5use std::hash::{Hash, Hasher};
6
7pub fn md5(value: &Value) -> Result<Value> {
9 let s = value.to_string();
10 let mut hasher = DefaultHasher::new();
12 s.hash(&mut hasher);
13 Ok(Value::String(format!("{:x}", hasher.finish())))
14}
15
16pub fn sha1(value: &Value) -> Result<Value> {
18 let s = value.to_string();
19 let mut hasher = DefaultHasher::new();
20 ("sha1".to_string() + &s).hash(&mut hasher);
21 Ok(Value::String(format!("{:x}", hasher.finish())))
22}
23
24pub fn sha256(value: &Value) -> Result<Value> {
26 let s = value.to_string();
27 let mut hasher = DefaultHasher::new();
28 ("sha256".to_string() + &s).hash(&mut hasher);
29 Ok(Value::String(format!("{:x}", hasher.finish())))
30}
31
32pub fn base64_encode(value: &Value) -> Result<Value> {
34 use base64::Engine;
35 let s = value.to_string();
36 let encoded = base64::engine::general_purpose::STANDARD.encode(s);
37 Ok(Value::String(encoded))
38}
39
40pub fn base64_decode(value: &Value) -> Result<Value> {
42 use base64::Engine;
43 let s = value.to_string();
44 let decoded = base64::engine::general_purpose::STANDARD
45 .decode(s)
46 .map_err(|e| crate::error::DdbError::FunctionError(format!("Base64 decode error: {}", e)))?;
47
48 let result = String::from_utf8(decoded)
49 .map_err(|e| crate::error::DdbError::FunctionError(format!("UTF-8 decode error: {}", e)))?;
50
51 Ok(Value::String(result))
52}
53
54pub fn url_encode(value: &Value) -> Result<Value> {
56 let s = value.to_string();
57 let encoded: String = s
58 .chars()
59 .map(|c| match c {
60 'A'..='Z' | 'a'..='z' | '0'..='9' | '-' | '_' | '.' | '~' => c.to_string(),
61 ' ' => "+".to_string(),
62 _ => format!("%{:02X}", c as u8),
63 })
64 .collect();
65 Ok(Value::String(encoded))
66}
67
68pub fn url_decode(value: &Value) -> Result<Value> {
70 let s = value.to_string();
71 let decoded = s.replace('+', " ");
72
73 let mut result = String::new();
75 let mut chars = decoded.chars().peekable();
76
77 while let Some(ch) = chars.next() {
78 if ch == '%' {
79 let hex: String = chars.by_ref().take(2).collect();
80 if let Ok(byte) = u8::from_str_radix(&hex, 16) {
81 result.push(byte as char);
82 } else {
83 result.push(ch);
84 result.push_str(&hex);
85 }
86 } else {
87 result.push(ch);
88 }
89 }
90
91 Ok(Value::String(result))
92}
93
94pub fn hash(value: &Value) -> Result<Value> {
96 let s = value.to_string();
97 let mut hasher = DefaultHasher::new();
98 s.hash(&mut hasher);
99 Ok(Value::Integer(hasher.finish() as i64))
100}
101
102pub fn split_part(string: &Value, delimiter: &Value, field_num: &Value) -> Result<Value> {
104 let s = string.to_string();
105 let delim = delimiter.to_string();
106 let n = field_num.as_i64()? as usize;
107
108 if n == 0 {
109 return Err(crate::error::DdbError::FunctionError(
110 "Field number must be >= 1".to_string(),
111 ));
112 }
113
114 let parts: Vec<&str> = s.split(&delim).collect();
115
116 if n > parts.len() {
117 return Ok(Value::String(String::new()));
118 }
119
120 Ok(Value::String(parts[n - 1].to_string()))
121}
122
123pub fn regexp_replace(string: &Value, pattern: &Value, replacement: &Value) -> Result<Value> {
125 let s = string.to_string();
126 let pat = pattern.to_string();
127 let repl = replacement.to_string();
128
129 let re = regex::Regex::new(&pat)
130 .map_err(|e| crate::error::DdbError::FunctionError(format!("Invalid regex: {}", e)))?;
131
132 Ok(Value::String(re.replace_all(&s, repl.as_str()).to_string()))
133}
134
135pub fn regexp_match(string: &Value, pattern: &Value) -> Result<Value> {
137 let s = string.to_string();
138 let pat = pattern.to_string();
139
140 let re = regex::Regex::new(&pat)
141 .map_err(|e| crate::error::DdbError::FunctionError(format!("Invalid regex: {}", e)))?;
142
143 Ok(Value::Boolean(re.is_match(&s)))
144}
145
146pub fn levenshtein(str1: &Value, str2: &Value) -> Result<Value> {
148 let s1 = str1.to_string();
149 let s2 = str2.to_string();
150
151 let len1 = s1.len();
152 let len2 = s2.len();
153
154 let mut matrix = vec![vec![0; len2 + 1]; len1 + 1];
155
156 for i in 0..=len1 {
157 matrix[i][0] = i;
158 }
159 for j in 0..=len2 {
160 matrix[0][j] = j;
161 }
162
163 for (i, c1) in s1.chars().enumerate() {
164 for (j, c2) in s2.chars().enumerate() {
165 let cost = if c1 == c2 { 0 } else { 1 };
166 matrix[i + 1][j + 1] = std::cmp::min(
167 std::cmp::min(matrix[i][j + 1] + 1, matrix[i + 1][j] + 1),
168 matrix[i][j] + cost,
169 );
170 }
171 }
172
173 Ok(Value::Integer(matrix[len1][len2] as i64))
174}
175
176#[cfg(test)]
177mod tests {
178 use super::*;
179
180 #[test]
181 fn test_hash() {
182 let h1 = hash(&Value::from("test")).unwrap();
183 let h2 = hash(&Value::from("test")).unwrap();
184 assert_eq!(h1, h2); }
186
187 #[test]
188 fn test_base64() {
189 let encoded = base64_encode(&Value::from("Hello World")).unwrap();
190 assert!(matches!(encoded, Value::String(_)));
191
192 let decoded = base64_decode(&encoded).unwrap();
193 assert_eq!(decoded, Value::from("Hello World"));
194 }
195
196 #[test]
197 fn test_split_part() {
198 let result = split_part(&Value::from("a,b,c"), &Value::from(","), &Value::Integer(2)).unwrap();
199 assert_eq!(result, Value::from("b"));
200 }
201
202 #[test]
203 fn test_regexp_match() {
204 let result = regexp_match(&Value::from("test123"), &Value::from(r"\d+")).unwrap();
205 assert_eq!(result, Value::Boolean(true));
206 }
207
208 #[test]
209 fn test_levenshtein() {
210 let dist = levenshtein(&Value::from("kitten"), &Value::from("sitting")).unwrap();
211 assert_eq!(dist, Value::Integer(3));
212 }
213}