ddb_core/functions/
string.rs1use super::Value;
3use crate::error::Result;
4
5pub fn concat(values: &[Value]) -> Result<Value> {
7 let result = values
8 .iter()
9 .map(|v| v.to_string())
10 .collect::<Vec<_>>()
11 .join("");
12 Ok(Value::String(result))
13}
14
15pub fn concat_ws(separator: &Value, values: &[Value]) -> Result<Value> {
17 let sep = separator.to_string();
18 let result = values
19 .iter()
20 .map(|v| v.to_string())
21 .collect::<Vec<_>>()
22 .join(&sep);
23 Ok(Value::String(result))
24}
25
26pub fn upper(value: &Value) -> Result<Value> {
28 Ok(Value::String(value.to_string().to_uppercase()))
29}
30
31pub fn lower(value: &Value) -> Result<Value> {
33 Ok(Value::String(value.to_string().to_lowercase()))
34}
35
36pub fn length(value: &Value) -> Result<Value> {
38 Ok(Value::Integer(value.to_string().len() as i64))
39}
40
41pub fn trim(value: &Value) -> Result<Value> {
43 Ok(Value::String(value.to_string().trim().to_string()))
44}
45
46pub fn ltrim(value: &Value) -> Result<Value> {
48 Ok(Value::String(value.to_string().trim_start().to_string()))
49}
50
51pub fn rtrim(value: &Value) -> Result<Value> {
53 Ok(Value::String(value.to_string().trim_end().to_string()))
54}
55
56pub fn substr(value: &Value, pos: &Value, len: Option<&Value>) -> Result<Value> {
58 let s = value.to_string();
59 let start = (pos.as_i64()? - 1).max(0) as usize; if start >= s.len() {
62 return Ok(Value::String(String::new()));
63 }
64
65 let result = if let Some(length) = len {
66 let length = length.as_i64()?.max(0) as usize;
67 s.chars().skip(start).take(length).collect()
68 } else {
69 s.chars().skip(start).collect()
70 };
71
72 Ok(Value::String(result))
73}
74
75pub fn left(value: &Value, len: &Value) -> Result<Value> {
77 let s = value.to_string();
78 let n = len.as_i64()?.max(0) as usize;
79 Ok(Value::String(s.chars().take(n).collect()))
80}
81
82pub fn right(value: &Value, len: &Value) -> Result<Value> {
84 let s = value.to_string();
85 let n = len.as_i64()?.max(0) as usize;
86 let chars: Vec<char> = s.chars().collect();
87 let start = chars.len().saturating_sub(n);
88 Ok(Value::String(chars[start..].iter().collect()))
89}
90
91pub fn replace(value: &Value, from: &Value, to: &Value) -> Result<Value> {
93 let s = value.to_string();
94 let from_str = from.to_string();
95 let to_str = to.to_string();
96 Ok(Value::String(s.replace(&from_str, &to_str)))
97}
98
99pub fn reverse(value: &Value) -> Result<Value> {
101 Ok(Value::String(value.to_string().chars().rev().collect()))
102}
103
104pub fn lpad(value: &Value, len: &Value, pad: &Value) -> Result<Value> {
106 let s = value.to_string();
107 let target_len = len.as_i64()? as usize;
108 let pad_str = pad.to_string();
109
110 if s.len() >= target_len {
111 return Ok(Value::String(s.chars().take(target_len).collect()));
112 }
113
114 let padding_needed = target_len - s.len();
115 let pad_repeated = pad_str.repeat((padding_needed / pad_str.len()) + 1);
116 let result = format!("{}{}", &pad_repeated[..padding_needed], s);
117
118 Ok(Value::String(result))
119}
120
121pub fn rpad(value: &Value, len: &Value, pad: &Value) -> Result<Value> {
123 let s = value.to_string();
124 let target_len = len.as_i64()? as usize;
125 let pad_str = pad.to_string();
126
127 if s.len() >= target_len {
128 return Ok(Value::String(s.chars().take(target_len).collect()));
129 }
130
131 let padding_needed = target_len - s.len();
132 let pad_repeated = pad_str.repeat((padding_needed / pad_str.len()) + 1);
133 let result = format!("{}{}", s, &pad_repeated[..padding_needed]);
134
135 Ok(Value::String(result))
136}
137
138pub fn position(substr: &Value, string: &Value) -> Result<Value> {
140 let s = string.to_string();
141 let sub = substr.to_string();
142
143 match s.find(&sub) {
144 Some(pos) => Ok(Value::Integer((pos + 1) as i64)), None => Ok(Value::Integer(0)),
146 }
147}
148
149pub fn repeat(value: &Value, count: &Value) -> Result<Value> {
151 let s = value.to_string();
152 let n = count.as_i64()?.max(0) as usize;
153 Ok(Value::String(s.repeat(n)))
154}
155
156pub fn space(count: &Value) -> Result<Value> {
158 let n = count.as_i64()?.max(0) as usize;
159 Ok(Value::String(" ".repeat(n)))
160}
161
162#[cfg(test)]
163mod tests {
164 use super::*;
165
166 #[test]
167 fn test_concat() {
168 let vals = vec![Value::from("Hello"), Value::from(" "), Value::from("World")];
169 assert_eq!(concat(&vals).unwrap(), Value::from("Hello World"));
170 }
171
172 #[test]
173 fn test_upper_lower() {
174 assert_eq!(upper(&Value::from("hello")).unwrap(), Value::from("HELLO"));
175 assert_eq!(lower(&Value::from("WORLD")).unwrap(), Value::from("world"));
176 }
177
178 #[test]
179 fn test_substr() {
180 let s = Value::from("Hello World");
181 assert_eq!(
182 substr(&s, &Value::Integer(1), Some(&Value::Integer(5))).unwrap(),
183 Value::from("Hello")
184 );
185 assert_eq!(
186 substr(&s, &Value::Integer(7), None).unwrap(),
187 Value::from("World")
188 );
189 }
190
191 #[test]
192 fn test_trim() {
193 assert_eq!(trim(&Value::from(" hello ")).unwrap(), Value::from("hello"));
194 assert_eq!(ltrim(&Value::from(" hello")).unwrap(), Value::from("hello"));
195 assert_eq!(rtrim(&Value::from("hello ")).unwrap(), Value::from("hello"));
196 }
197
198 #[test]
199 fn test_replace() {
200 assert_eq!(
201 replace(&Value::from("hello world"), &Value::from("world"), &Value::from("rust")).unwrap(),
202 Value::from("hello rust")
203 );
204 }
205}