1use std::fmt;
10
11#[derive(Debug, Clone, PartialEq, Eq)]
13pub enum Operator {
14 Equal,
16 NotEqual,
18 GreaterThan,
20 LessThan,
22 GreaterThanOrEqual,
24 LessThanOrEqual,
26 Contains,
28}
29
30impl fmt::Display for Operator {
31 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32 match self {
33 Operator::Equal => write!(f, "="),
34 Operator::NotEqual => write!(f, "!="),
35 Operator::GreaterThan => write!(f, ">"),
36 Operator::LessThan => write!(f, "<"),
37 Operator::GreaterThanOrEqual => write!(f, ">="),
38 Operator::LessThanOrEqual => write!(f, "<="),
39 Operator::Contains => write!(f, "contains"),
40 }
41 }
42}
43
44#[derive(Debug, Clone, PartialEq)]
46pub enum QueryValue {
47 String(String),
49 Number(f64),
51 Duration(u64),
53}
54
55impl fmt::Display for QueryValue {
56 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57 match self {
58 QueryValue::String(s) => write!(f, "\"{}\"", s),
59 QueryValue::Number(n) => write!(f, "{}", n),
60 QueryValue::Duration(d) => write!(f, "{}ms", d),
61 }
62 }
63}
64
65#[derive(Debug, Clone, PartialEq)]
67pub struct QueryPredicate {
68 pub field: String,
70 pub operator: Operator,
72 pub value: QueryValue,
74}
75
76impl fmt::Display for QueryPredicate {
77 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78 write!(f, "{} {} {}", self.field, self.operator, self.value)
79 }
80}
81
82#[derive(Debug, Clone, PartialEq, Eq)]
84pub enum QueryError {
85 EmptyQuery,
87 InvalidSyntax(String),
89 UnknownOperator(String),
91 InvalidValue(String),
93}
94
95impl fmt::Display for QueryError {
96 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
97 match self {
98 QueryError::EmptyQuery => write!(f, "Query string is empty"),
99 QueryError::InvalidSyntax(msg) => write!(f, "Invalid syntax: {}", msg),
100 QueryError::UnknownOperator(op) => write!(f, "Unknown operator: {}", op),
101 QueryError::InvalidValue(msg) => write!(f, "Invalid value: {}", msg),
102 }
103 }
104}
105
106impl std::error::Error for QueryError {}
107
108pub fn parse_query(input: &str) -> Result<Vec<QueryPredicate>, QueryError> {
121 let input = input.trim();
122 if input.is_empty() {
123 return Err(QueryError::EmptyQuery);
124 }
125
126 let mut predicates = Vec::new();
127
128 let parts: Vec<&str> = input.split(" AND ").collect();
130
131 for part in parts {
132 let predicate = parse_single_predicate(part.trim())?;
133 predicates.push(predicate);
134 }
135
136 Ok(predicates)
137}
138
139fn parse_single_predicate(input: &str) -> Result<QueryPredicate, QueryError> {
140 let (field, operator, value_str) = if let Some(pos) = input.find(" contains ") {
142 let field = input[..pos].trim();
143 let value = input[pos + 10..].trim();
144 (field, Operator::Contains, value)
145 } else if let Some(pos) = input.find(" >= ") {
146 let field = input[..pos].trim();
147 let value = input[pos + 4..].trim();
148 (field, Operator::GreaterThanOrEqual, value)
149 } else if let Some(pos) = input.find(" <= ") {
150 let field = input[..pos].trim();
151 let value = input[pos + 4..].trim();
152 (field, Operator::LessThanOrEqual, value)
153 } else if let Some(pos) = input.find(" != ") {
154 let field = input[..pos].trim();
155 let value = input[pos + 4..].trim();
156 (field, Operator::NotEqual, value)
157 } else if let Some(pos) = input.find(" > ") {
158 let field = input[..pos].trim();
159 let value = input[pos + 3..].trim();
160 (field, Operator::GreaterThan, value)
161 } else if let Some(pos) = input.find(" < ") {
162 let field = input[..pos].trim();
163 let value = input[pos + 3..].trim();
164 (field, Operator::LessThan, value)
165 } else if let Some(pos) = input.find(" = ") {
166 let field = input[..pos].trim();
167 let value = input[pos + 3..].trim();
168 (field, Operator::Equal, value)
169 } else {
170 return Err(QueryError::InvalidSyntax(
171 "No valid operator found. Expected: =, !=, >, <, >=, <=, contains".to_string(),
172 ));
173 };
174
175 if field.is_empty() {
176 return Err(QueryError::InvalidSyntax("Field name is empty".to_string()));
177 }
178
179 if value_str.is_empty() {
180 return Err(QueryError::InvalidValue("Value is empty".to_string()));
181 }
182
183 let value = parse_value(value_str)?;
184
185 Ok(QueryPredicate {
186 field: field.to_string(),
187 operator,
188 value,
189 })
190}
191
192fn parse_value(input: &str) -> Result<QueryValue, QueryError> {
193 let input = input.trim();
194
195 if input.is_empty() {
196 return Err(QueryError::InvalidValue("Value is empty".to_string()));
197 }
198
199 if (input.starts_with('"') && input.ends_with('"'))
201 || (input.starts_with('\'') && input.ends_with('\''))
202 {
203 if input.len() < 2 {
204 return Err(QueryError::InvalidValue(
205 "Quoted string is too short".to_string(),
206 ));
207 }
208 return Ok(QueryValue::String(input[1..input.len() - 1].to_string()));
209 }
210
211 if let Some(num_part) = input.strip_suffix("ms") {
213 match num_part.parse::<u64>() {
214 Ok(n) => return Ok(QueryValue::Duration(n)),
215 Err(_) => {
216 return Err(QueryError::InvalidValue(format!(
217 "Invalid duration format: {}",
218 input
219 )))
220 },
221 }
222 }
223
224 if let Some(num_part) = input.strip_suffix('s') {
225 match num_part.parse::<u64>() {
226 Ok(n) => return Ok(QueryValue::Duration(n * 1000)),
227 Err(_) => {
228 return Err(QueryError::InvalidValue(format!(
229 "Invalid duration format: {}",
230 input
231 )))
232 },
233 }
234 }
235
236 if let Ok(n) = input.parse::<f64>() {
238 return Ok(QueryValue::Number(n));
239 }
240
241 Err(QueryError::InvalidValue(format!(
243 "String values must be quoted: {}",
244 input
245 )))
246}
247
248#[cfg(test)]
249mod tests {
250 use super::*;
251
252 #[test]
253 fn test_parse_simple_equality() {
254 let result = parse_query("severity = \"ERROR\"").unwrap();
255 assert_eq!(result.len(), 1);
256 assert_eq!(result[0].field, "severity");
257 assert_eq!(result[0].operator, Operator::Equal);
258 assert_eq!(result[0].value, QueryValue::String("ERROR".to_string()));
259 }
260
261 #[test]
262 fn test_parse_quoted_string() {
263 let result = parse_query("name = \"my service\"").unwrap();
264 assert_eq!(result.len(), 1);
265 assert_eq!(result[0].field, "name");
266 assert_eq!(result[0].operator, Operator::Equal);
267 assert_eq!(
268 result[0].value,
269 QueryValue::String("my service".to_string())
270 );
271 }
272
273 #[test]
274 fn test_parse_duration_ms() {
275 let result = parse_query("duration > 500ms").unwrap();
276 assert_eq!(result.len(), 1);
277 assert_eq!(result[0].field, "duration");
278 assert_eq!(result[0].operator, Operator::GreaterThan);
279 assert_eq!(result[0].value, QueryValue::Duration(500));
280 }
281
282 #[test]
283 fn test_parse_duration_seconds() {
284 let result = parse_query("duration < 2s").unwrap();
285 assert_eq!(result.len(), 1);
286 assert_eq!(result[0].field, "duration");
287 assert_eq!(result[0].operator, Operator::LessThan);
288 assert_eq!(result[0].value, QueryValue::Duration(2000));
289 }
290
291 #[test]
292 fn test_parse_numeric_value() {
293 let result = parse_query("count >= 100").unwrap();
294 assert_eq!(result.len(), 1);
295 assert_eq!(result[0].field, "count");
296 assert_eq!(result[0].operator, Operator::GreaterThanOrEqual);
297 assert_eq!(result[0].value, QueryValue::Number(100.0));
298 }
299
300 #[test]
301 fn test_parse_contains_operator() {
302 let result = parse_query("name contains \"chat\"").unwrap();
303 assert_eq!(result.len(), 1);
304 assert_eq!(result[0].field, "name");
305 assert_eq!(result[0].operator, Operator::Contains);
306 assert_eq!(result[0].value, QueryValue::String("chat".to_string()));
307 }
308
309 #[test]
310 fn test_parse_dotted_field_name() {
311 let result = parse_query("gen_ai.system = \"anthropic\"").unwrap();
312 assert_eq!(result.len(), 1);
313 assert_eq!(result[0].field, "gen_ai.system");
314 assert_eq!(result[0].operator, Operator::Equal);
315 assert_eq!(result[0].value, QueryValue::String("anthropic".to_string()));
316 }
317
318 #[test]
319 fn test_parse_not_equal() {
320 let result = parse_query("status != 200").unwrap();
321 assert_eq!(result.len(), 1);
322 assert_eq!(result[0].field, "status");
323 assert_eq!(result[0].operator, Operator::NotEqual);
324 assert_eq!(result[0].value, QueryValue::Number(200.0));
325 }
326
327 #[test]
328 fn test_parse_less_than_or_equal() {
329 let result = parse_query("latency <= 1000ms").unwrap();
330 assert_eq!(result.len(), 1);
331 assert_eq!(result[0].field, "latency");
332 assert_eq!(result[0].operator, Operator::LessThanOrEqual);
333 assert_eq!(result[0].value, QueryValue::Duration(1000));
334 }
335
336 #[test]
337 fn test_parse_multiple_predicates() {
338 let result = parse_query("severity = \"ERROR\" AND duration > 500ms").unwrap();
339 assert_eq!(result.len(), 2);
340 assert_eq!(result[0].field, "severity");
341 assert_eq!(result[0].operator, Operator::Equal);
342 assert_eq!(result[1].field, "duration");
343 assert_eq!(result[1].operator, Operator::GreaterThan);
344 }
345
346 #[test]
347 fn test_empty_query() {
348 let result = parse_query("");
349 assert!(result.is_err());
350 assert_eq!(result.unwrap_err(), QueryError::EmptyQuery);
351 }
352
353 #[test]
354 fn test_invalid_syntax_no_operator() {
355 let result = parse_query("severity ERROR");
356 assert!(result.is_err());
357 match result.unwrap_err() {
358 QueryError::InvalidSyntax(_) => {},
359 _ => panic!("Expected InvalidSyntax error"),
360 }
361 }
362
363 #[test]
364 fn test_invalid_duration_format() {
365 let result = parse_query("duration > \"abc\"");
366 assert!(result.is_ok());
368
369 let result2 = parse_query("duration > abcms");
371 assert!(result2.is_err());
372 match result2.unwrap_err() {
373 QueryError::InvalidValue(_) => {},
374 _ => panic!("Expected InvalidValue error"),
375 }
376 }
377
378 #[test]
379 fn test_empty_field_name() {
380 let result = parse_query(" = ERROR");
381 assert!(result.is_err());
382 match result.unwrap_err() {
383 QueryError::InvalidSyntax(_) => {},
384 _ => panic!("Expected InvalidSyntax error"),
385 }
386 }
387
388 #[test]
389 fn test_empty_value() {
390 let result = parse_query("severity = \"\"");
392 assert!(result.is_ok()); let result2 = parse_query("severity =");
396 assert!(result2.is_err());
397 }
400}