gitql_core/values/
text.rs1use std::any::Any;
2use std::cmp::Ordering;
3
4use gitql_ast::operator::GroupComparisonOperator;
5use regex::Regex;
6use regex::RegexBuilder;
7
8use gitql_ast::types::text::TextType;
9use gitql_ast::types::DataType;
10
11use super::base::Value;
12use super::boolean::BoolValue;
13use super::converters::string_literal_to_boolean;
14use super::converters::string_literal_to_date;
15use super::converters::string_literal_to_date_time;
16use super::converters::string_literal_to_time;
17
18#[derive(Clone)]
19pub struct TextValue {
20 pub value: String,
21}
22
23impl TextValue {
24 pub fn new(value: String) -> Self {
25 TextValue { value }
26 }
27
28 pub fn empty() -> Self {
29 TextValue {
30 value: String::default(),
31 }
32 }
33}
34
35impl Value for TextValue {
36 fn literal(&self) -> String {
37 self.value.to_string()
38 }
39
40 fn equals(&self, other: &Box<dyn Value>) -> bool {
41 if let Some(other_text) = other.as_any().downcast_ref::<TextValue>() {
42 return self.value == other_text.value;
43 }
44 false
45 }
46
47 fn compare(&self, other: &Box<dyn Value>) -> Option<Ordering> {
48 if let Some(other_text) = other.as_any().downcast_ref::<TextValue>() {
49 return self.value.partial_cmp(&other_text.value);
50 }
51 None
52 }
53
54 fn data_type(&self) -> Box<dyn DataType> {
55 Box::new(TextType)
56 }
57
58 fn as_any(&self) -> &dyn Any {
59 self
60 }
61
62 fn eq_op(&self, other: &Box<dyn Value>) -> Result<Box<dyn Value>, String> {
63 if let Some(other_text) = other.as_any().downcast_ref::<TextValue>() {
64 return Ok(Box::new(BoolValue::new(self.value == other_text.value)));
65 }
66 Err("Unexpected type to perform `=` with".to_string())
67 }
68
69 fn group_eq_op(
70 &self,
71 other: &Box<dyn Value>,
72 group_op: &GroupComparisonOperator,
73 ) -> Result<Box<dyn Value>, String> {
74 if other.is_array_of(|element_type| element_type.is_text()) {
75 let elements = &other.as_array().unwrap();
76 let mut matches_count = 0;
77 for element in elements.iter() {
78 if self.value == element.as_text().unwrap() {
79 matches_count += 1;
80 if GroupComparisonOperator::Any.eq(group_op) {
81 break;
82 }
83 }
84 }
85
86 let result = match group_op {
87 GroupComparisonOperator::All => matches_count == elements.len(),
88 GroupComparisonOperator::Any => matches_count > 0,
89 };
90
91 return Ok(Box::new(BoolValue::new(result)));
92 }
93 Err("Unexpected type to perform `=` with".to_string())
94 }
95
96 fn bang_eq_op(&self, other: &Box<dyn Value>) -> Result<Box<dyn Value>, String> {
97 if let Some(other_text) = other.as_any().downcast_ref::<TextValue>() {
98 return Ok(Box::new(BoolValue::new(self.value != other_text.value)));
99 }
100 Err("Unexpected type to perform `!=` with".to_string())
101 }
102
103 fn group_bang_eq_op(
104 &self,
105 other: &Box<dyn Value>,
106 group_op: &GroupComparisonOperator,
107 ) -> Result<Box<dyn Value>, String> {
108 if other.is_array_of(|element_type| element_type.is_text()) {
109 let elements = &other.as_array().unwrap();
110 let mut matches_count = 0;
111 for element in elements.iter() {
112 if self.value != element.as_text().unwrap() {
113 matches_count += 1;
114 if GroupComparisonOperator::Any.eq(group_op) {
115 break;
116 }
117 }
118 }
119
120 let result = match group_op {
121 GroupComparisonOperator::All => matches_count == elements.len(),
122 GroupComparisonOperator::Any => matches_count > 0,
123 };
124
125 return Ok(Box::new(BoolValue::new(result)));
126 }
127 Err("Unexpected type to perform `!=` with".to_string())
128 }
129
130 fn gt_op(&self, other: &Box<dyn Value>) -> Result<Box<dyn Value>, String> {
131 if let Some(other_text) = other.as_any().downcast_ref::<TextValue>() {
132 return Ok(Box::new(BoolValue::new(self.value > other_text.value)));
133 }
134 Err("Unexpected type to perform `>` with".to_string())
135 }
136
137 fn group_gt_op(
138 &self,
139 other: &Box<dyn Value>,
140 group_op: &GroupComparisonOperator,
141 ) -> Result<Box<dyn Value>, String> {
142 if other.is_array_of(|element_type| element_type.is_text()) {
143 let elements = &other.as_array().unwrap();
144 let mut matches_count = 0;
145 for element in elements.iter() {
146 if self.value > element.as_text().unwrap() {
147 matches_count += 1;
148 if GroupComparisonOperator::Any.eq(group_op) {
149 break;
150 }
151 }
152 }
153
154 let result = match group_op {
155 GroupComparisonOperator::All => matches_count == elements.len(),
156 GroupComparisonOperator::Any => matches_count > 0,
157 };
158
159 return Ok(Box::new(BoolValue::new(result)));
160 }
161 Err("Unexpected type to perform `>` with".to_string())
162 }
163
164 fn gte_op(&self, other: &Box<dyn Value>) -> Result<Box<dyn Value>, String> {
165 if let Some(other_text) = other.as_any().downcast_ref::<TextValue>() {
166 return Ok(Box::new(BoolValue::new(self.value >= other_text.value)));
167 }
168 Err("Unexpected type to perform `>=` with".to_string())
169 }
170
171 fn group_gte_op(
172 &self,
173 other: &Box<dyn Value>,
174 group_op: &GroupComparisonOperator,
175 ) -> Result<Box<dyn Value>, String> {
176 if other.is_array_of(|element_type| element_type.is_text()) {
177 let elements = &other.as_array().unwrap();
178 let mut matches_count = 0;
179 for element in elements.iter() {
180 if self.value >= element.as_text().unwrap() {
181 matches_count += 1;
182 if GroupComparisonOperator::Any.eq(group_op) {
183 break;
184 }
185 }
186 }
187
188 let result = match group_op {
189 GroupComparisonOperator::All => matches_count == elements.len(),
190 GroupComparisonOperator::Any => matches_count > 0,
191 };
192
193 return Ok(Box::new(BoolValue::new(result)));
194 }
195 Err("Unexpected type to perform `>=` with".to_string())
196 }
197
198 fn lt_op(&self, other: &Box<dyn Value>) -> Result<Box<dyn Value>, String> {
199 if let Some(other_text) = other.as_any().downcast_ref::<TextValue>() {
200 return Ok(Box::new(BoolValue::new(self.value < other_text.value)));
201 }
202 Err("Unexpected type to perform `<` with".to_string())
203 }
204
205 fn group_lt_op(
206 &self,
207 other: &Box<dyn Value>,
208 group_op: &GroupComparisonOperator,
209 ) -> Result<Box<dyn Value>, String> {
210 if other.is_array_of(|element_type| element_type.is_text()) {
211 let elements = &other.as_array().unwrap();
212 let mut matches_count = 0;
213 for element in elements.iter() {
214 if self.value < element.as_text().unwrap() {
215 matches_count += 1;
216 if GroupComparisonOperator::Any.eq(group_op) {
217 break;
218 }
219 }
220 }
221
222 let result = match group_op {
223 GroupComparisonOperator::All => matches_count == elements.len(),
224 GroupComparisonOperator::Any => matches_count > 0,
225 };
226
227 return Ok(Box::new(BoolValue::new(result)));
228 }
229 Err("Unexpected type to perform `<` with".to_string())
230 }
231
232 fn lte_op(&self, other: &Box<dyn Value>) -> Result<Box<dyn Value>, String> {
233 if let Some(other_text) = other.as_any().downcast_ref::<TextValue>() {
234 return Ok(Box::new(BoolValue::new(self.value <= other_text.value)));
235 }
236 Err("Unexpected type to perform `<=` with".to_string())
237 }
238
239 fn group_lte_op(
240 &self,
241 other: &Box<dyn Value>,
242 group_op: &GroupComparisonOperator,
243 ) -> Result<Box<dyn Value>, String> {
244 if other.is_array_of(|element_type| element_type.is_text()) {
245 let elements = &other.as_array().unwrap();
246 let mut matches_count = 0;
247 for element in elements.iter() {
248 if self.value <= element.as_text().unwrap() {
249 matches_count += 1;
250 if GroupComparisonOperator::Any.eq(group_op) {
251 break;
252 }
253 }
254 }
255
256 let result = match group_op {
257 GroupComparisonOperator::All => matches_count == elements.len(),
258 GroupComparisonOperator::Any => matches_count > 0,
259 };
260
261 return Ok(Box::new(BoolValue::new(result)));
262 }
263 Err("Unexpected type to perform `<=` with".to_string())
264 }
265
266 fn like_op(&self, other: &Box<dyn Value>) -> Result<Box<dyn Value>, String> {
267 let pattern_text = other.as_text().unwrap();
268 let pattern = &format!(
269 "^{}$",
270 pattern_text
271 .to_lowercase()
272 .replace('%', ".*")
273 .replace('_', ".")
274 );
275
276 let regex_builder = RegexBuilder::new(pattern)
277 .multi_line(true)
278 .unicode(true)
279 .build();
280
281 match regex_builder {
282 Ok(regex) => {
283 let is_match = regex.is_match(&self.value.to_lowercase());
284 Ok(Box::new(BoolValue { value: is_match }))
285 }
286 Err(error_message) => Err(error_message.to_string()),
287 }
288 }
289
290 fn glob_op(&self, other: &Box<dyn Value>) -> Result<Box<dyn Value>, String> {
291 let pattern_text = other.as_text().unwrap();
292 let pattern = &format!(
293 "^{}$",
294 pattern_text
295 .replace('.', "\\.")
296 .replace('*', ".*")
297 .replace('?', ".")
298 );
299
300 match Regex::new(pattern) {
301 Ok(regex) => {
302 let is_match = regex.is_match(&self.value);
303 Ok(Box::new(BoolValue { value: is_match }))
304 }
305 Err(error_message) => Err(error_message.to_string()),
306 }
307 }
308
309 fn regexp_op(&self, other: &Box<dyn Value>) -> Result<Box<dyn Value>, String> {
310 let pattern_text = other.as_text().unwrap();
311 let pattern = &format!(
312 "^{}$",
313 pattern_text
314 .to_lowercase()
315 .replace('%', ".*")
316 .replace('_', ".")
317 );
318
319 let regex_builder = RegexBuilder::new(pattern)
320 .multi_line(true)
321 .unicode(true)
322 .build();
323
324 match regex_builder {
325 Ok(regex) => {
326 let is_match = regex.is_match(&self.value.to_lowercase());
327 Ok(Box::new(BoolValue { value: is_match }))
328 }
329 Err(error_message) => Err(error_message.to_string()),
330 }
331 }
332
333 fn cast_op(&self, target_type: &Box<dyn DataType>) -> Result<Box<dyn Value>, String> {
334 if target_type.is_bool() {
335 return Ok(string_literal_to_boolean(&self.value));
336 }
337
338 if target_type.is_time() {
339 return Ok(string_literal_to_time(&self.value));
340 }
341
342 if target_type.is_date() {
343 return Ok(string_literal_to_date(&self.value));
344 }
345
346 if target_type.is_date_time() {
347 return Ok(string_literal_to_date_time(&self.value));
348 }
349
350 Err("Unexpected value to perform `CAST` with".to_string())
351 }
352}