1use std::io;
8use std::sync::Arc;
9
10use regex::Regex;
11
12use crate::matchers::{EqualMatcher, LabelMatcher, NotEqualMatcher, NotRegexMatcher, RegexMatcher};
13use crate::storage::Storage;
14
15#[derive(Clone)]
17pub struct SimpleQueryEngine {
18 storage: Arc<dyn Storage>,
19}
20
21impl SimpleQueryEngine {
22 pub fn new(storage: Arc<dyn Storage>) -> Self {
32 Self { storage }
33 }
34
35 pub fn query(&self, query: &str, start: i64, end: i64) -> io::Result<QueryResult> {
37 let selector = Self::parse_selector(query)?;
38 let series = self.storage.query_series(&selector.matchers);
39
40 let mut result_series = Vec::new();
41 for ts in series {
42 let samples = ts.samples_in_range(start, end);
43 if !samples.is_empty() {
44 result_series.push(QueryResultSeries {
45 labels: ts.labels.clone(),
46 samples: samples.into_iter().cloned().collect(),
47 });
48 }
49 }
50
51 Ok(QueryResult { series: result_series })
52 }
53
54 fn parse_selector(query: &str) -> io::Result<MetricSelector> {
56 let query = query.trim();
57
58 let (metric_name, labels_part) = query.find('{').map_or((Some(query), None), |brace_pos| {
60 let name = query[..brace_pos].trim();
61 let labels = &query[brace_pos..];
62 (Some(name), Some(labels))
63 });
64
65 let mut matchers: Vec<Arc<dyn LabelMatcher>> = Vec::new();
66
67 if let Some(name) = metric_name {
69 if !name.is_empty() {
70 matchers.push(Arc::new(EqualMatcher::new("__name__", name)));
71 }
72 }
73
74 if let Some(labels) = labels_part {
76 let label_matchers = Self::parse_label_matchers(labels)?;
77 matchers.extend(label_matchers);
78 }
79
80 Ok(MetricSelector { matchers })
81 }
82
83 fn parse_label_matchers(labels_str: &str) -> io::Result<Vec<Arc<dyn LabelMatcher>>> {
85 let labels_str = labels_str.trim();
86 if !labels_str.starts_with('{') || !labels_str.ends_with('}') {
87 return Err(io::Error::new(io::ErrorKind::InvalidInput, "invalid label syntax"));
88 }
89
90 let inner = &labels_str[1..labels_str.len() - 1];
91 if inner.trim().is_empty() {
92 return Ok(Vec::new());
93 }
94
95 let mut matchers = Vec::new();
96 let parts = Self::split_label_expressions(inner);
97
98 for part in parts {
99 let matcher = Self::parse_single_label_matcher(&part)?;
100 matchers.push(matcher);
101 }
102
103 Ok(matchers)
104 }
105
106 fn split_label_expressions(input: &str) -> Vec<String> {
108 let mut parts = Vec::new();
109 let mut current = String::new();
110 let mut in_quotes = false;
111 let mut escape_next = false;
112
113 for ch in input.chars() {
114 if escape_next {
115 current.push(ch);
116 escape_next = false;
117 continue;
118 }
119
120 match ch {
121 '\\' => {
122 escape_next = true;
123 current.push(ch);
124 }
125 '"' => {
126 in_quotes = !in_quotes;
127 current.push(ch);
128 }
129 ',' if !in_quotes => {
130 parts.push(current.trim().to_string());
131 current.clear();
132 }
133 _ => {
134 current.push(ch);
135 }
136 }
137 }
138
139 if !current.trim().is_empty() {
140 parts.push(current.trim().to_string());
141 }
142
143 parts
144 }
145
146 fn parse_single_label_matcher(expr: &str) -> io::Result<Arc<dyn LabelMatcher>> {
148 let expr = expr.trim();
149
150 if let Some(pos) = expr.find("!=") {
152 let name = expr[..pos].trim().to_string();
153 let value = Self::parse_quoted_value(&expr[pos + 2..])?;
154 return Ok(Arc::new(NotEqualMatcher::new(name, value)));
155 }
156
157 if let Some(pos) = expr.find("!~") {
158 let name = expr[..pos].trim().to_string();
159 let pattern_str = Self::parse_quoted_value(&expr[pos + 2..])?;
160 let pattern = Regex::new(&pattern_str).map_err(|e| {
161 io::Error::new(io::ErrorKind::InvalidInput, format!("invalid regex: {e}"))
162 })?;
163 return Ok(Arc::new(NotRegexMatcher::new(name, pattern)));
164 }
165
166 if let Some(pos) = expr.find("=~") {
167 let name = expr[..pos].trim().to_string();
168 let pattern_str = Self::parse_quoted_value(&expr[pos + 2..])?;
169 let pattern = Regex::new(&pattern_str).map_err(|e| {
170 io::Error::new(io::ErrorKind::InvalidInput, format!("invalid regex: {e}"))
171 })?;
172 return Ok(Arc::new(RegexMatcher::new(name, pattern)));
173 }
174
175 if let Some(pos) = expr.find('=') {
176 let name = expr[..pos].trim().to_string();
177 let value = Self::parse_quoted_value(&expr[pos + 1..])?;
178 return Ok(Arc::new(EqualMatcher::new(name, value)));
179 }
180
181 Err(io::Error::new(io::ErrorKind::InvalidInput, format!("invalid label matcher: {expr}")))
182 }
183
184 fn parse_quoted_value(input: &str) -> io::Result<String> {
186 let input = input.trim();
187 if input.starts_with('"') && input.ends_with('"') && input.len() >= 2 {
188 Ok(input[1..input.len() - 1].to_string())
189 } else {
190 Err(io::Error::new(
191 io::ErrorKind::InvalidInput,
192 format!("expected quoted value: {input}"),
193 ))
194 }
195 }
196}
197
198#[derive(Debug)]
199struct MetricSelector {
200 matchers: Vec<Arc<dyn LabelMatcher>>,
201}
202
203#[derive(Debug)]
205pub struct QueryResult {
206 pub series: Vec<QueryResultSeries>,
207}
208
209#[derive(Debug)]
210pub struct QueryResultSeries {
211 pub labels: Vec<crate::storage::Label>,
212 pub samples: Vec<crate::storage::Sample>,
213}
214
215#[cfg(test)]
216mod tests {
217 use super::*;
218 use crate::storage::{Label, MemoryStorage, Sample, TimeSeries};
219
220 #[test]
222 fn test_parse_simple_selector() {
223 let _engine = SimpleQueryEngine::new(Arc::new(MemoryStorage::new()));
224
225 let selector = SimpleQueryEngine::parse_selector("up").expect("valid syntax");
227 assert_eq!(selector.matchers.len(), 1);
228
229 let selector =
231 SimpleQueryEngine::parse_selector(r#"http_requests{job="api",method!="POST"}"#)
232 .expect("valid syntax");
233 assert_eq!(selector.matchers.len(), 3); }
235
236 #[test]
238 fn test_split_label_expressions() {
239 let parts = SimpleQueryEngine::split_label_expressions(r#"a="b",c!="d",e=~"regex""#);
240 assert_eq!(parts.len(), 3);
241 assert_eq!(parts[0], r#"a="b""#);
242 assert_eq!(parts[1], r#"c!="d""#);
243 assert_eq!(parts[2], r#"e=~"regex""#);
244 }
245
246 #[test]
248 fn test_parse_label_matcher() {
249 let matcher =
250 SimpleQueryEngine::parse_single_label_matcher(r#"job="api""#).expect("valid syntax");
251 assert_eq!(matcher.label_name(), "job");
252
253 let labels = vec![crate::storage::Label::new("job", "api")];
254 assert!(matcher.matches(&labels));
255
256 let matcher = SimpleQueryEngine::parse_single_label_matcher(r#"method!="POST""#)
257 .expect("valid syntax");
258 assert_eq!(matcher.label_name(), "method");
259
260 let labels = vec![crate::storage::Label::new("method", "GET")];
261 assert!(matcher.matches(&labels));
262 }
263
264 #[test]
266 fn test_query_with_storage() {
267 let storage = Arc::new(MemoryStorage::new());
268 let engine = SimpleQueryEngine::new(storage.clone());
269
270 let labels = vec![
272 Label::new("__name__", "http_requests"),
273 Label::new("job", "api"),
274 Label::new("method", "GET"),
275 ];
276 let mut ts = TimeSeries::new(labels);
277 ts.add_sample(Sample::new(1000, 10.0));
278 ts.add_sample(Sample::new(2000, 20.0));
279 storage.add_series(ts);
280
281 let result = engine.query(r#"http_requests{job="api"}"#, 0, 3000).expect("valid query");
283 assert_eq!(result.series.len(), 1);
284 assert_eq!(result.series[0].samples.len(), 2);
285 }
286
287 #[test]
289 fn test_parse_edge_cases() {
290 let result = SimpleQueryEngine::parse_selector("{}");
292 assert!(result.is_ok());
293 let selector = result.unwrap();
294 assert_eq!(selector.matchers.len(), 0);
295
296 let result = SimpleQueryEngine::parse_selector("cpu_usage");
298 assert!(result.is_ok());
299 let selector = result.unwrap();
300 assert_eq!(selector.matchers.len(), 1);
301 assert_eq!(selector.matchers[0].label_name(), "__name__");
302
303 let result = SimpleQueryEngine::parse_selector(r#"{job="api"}"#);
305 assert!(result.is_ok());
306 let selector = result.unwrap();
307 assert_eq!(selector.matchers.len(), 1);
308 assert_eq!(selector.matchers[0].label_name(), "job");
309
310 let result = SimpleQueryEngine::parse_selector(r#" cpu_usage { job = "api" } "#);
312 assert!(result.is_ok());
313 let selector = result.unwrap();
314 assert_eq!(selector.matchers.len(), 2);
315 }
316
317 #[test]
319 fn test_parse_error_cases() {
320 let result = SimpleQueryEngine::parse_selector(r#"metric{job="api""#);
322 assert!(result.is_err());
323
324 let result = SimpleQueryEngine::parse_label_matchers(r#"job="api"}"#);
326 assert!(result.is_err());
327
328 let result = SimpleQueryEngine::parse_quoted_value("unquoted");
330 assert!(result.is_err());
331
332 let result = SimpleQueryEngine::parse_quoted_value(r#""missing_close"#);
334 assert!(result.is_err());
335
336 let result = SimpleQueryEngine::parse_single_label_matcher("invalid_syntax");
338 assert!(result.is_err());
339
340 let result = SimpleQueryEngine::parse_single_label_matcher(r#"test=~"[invalid""#);
342 assert!(result.is_err());
343 }
344
345 #[test]
347 fn test_regex_matchers() {
348 let result = SimpleQueryEngine::parse_single_label_matcher(r#"instance=~"server.*""#);
350 assert!(result.is_ok());
351 let matcher = result.unwrap();
352 assert_eq!(matcher.label_name(), "instance");
353
354 let labels = vec![crate::storage::Label::new("instance", "server1")];
355 assert!(matcher.matches(&labels));
356
357 let labels = vec![crate::storage::Label::new("instance", "client1")];
358 assert!(!matcher.matches(&labels));
359
360 let result = SimpleQueryEngine::parse_single_label_matcher(r#"method!~"POST|PUT""#);
362 assert!(result.is_ok());
363 let matcher = result.unwrap();
364 assert_eq!(matcher.label_name(), "method");
365
366 let labels = vec![crate::storage::Label::new("method", "GET")];
367 assert!(matcher.matches(&labels));
368
369 let labels = vec![crate::storage::Label::new("method", "POST")];
370 assert!(!matcher.matches(&labels));
371 }
372
373 #[test]
375 fn test_complex_label_splitting() {
376 let parts =
378 SimpleQueryEngine::split_label_expressions(r#"a="value with \"quotes\"",b="normal""#);
379 assert_eq!(parts.len(), 2);
380 assert_eq!(parts[0], r#"a="value with \"quotes\"""#);
381 assert_eq!(parts[1], r#"b="normal""#);
382
383 let parts = SimpleQueryEngine::split_label_expressions(
385 r#"description="contains, comma",job="api""#,
386 );
387 assert_eq!(parts.len(), 2);
388 assert_eq!(parts[0], r#"description="contains, comma""#);
389 assert_eq!(parts[1], r#"job="api""#);
390
391 let parts = SimpleQueryEngine::split_label_expressions("");
393 assert_eq!(parts.len(), 0);
394
395 let parts = SimpleQueryEngine::split_label_expressions(r#"job="api""#);
397 assert_eq!(parts.len(), 1);
398 assert_eq!(parts[0], r#"job="api""#);
399 }
400
401 #[test]
403 fn test_query_with_time_filtering() {
404 let storage = Arc::new(MemoryStorage::new());
405 let engine = SimpleQueryEngine::new(storage.clone());
406
407 let labels = vec![Label::new("__name__", "cpu_usage"), Label::new("job", "api")];
409 let mut ts = TimeSeries::new(labels);
410 ts.add_sample(Sample::new(1000, 10.0));
411 ts.add_sample(Sample::new(2000, 20.0));
412 ts.add_sample(Sample::new(3000, 30.0));
413 ts.add_sample(Sample::new(4000, 40.0));
414 storage.add_series(ts);
415
416 let result = engine.query("cpu_usage", 1500, 3500).expect("valid query");
418 assert_eq!(result.series.len(), 1);
419 assert_eq!(result.series[0].samples.len(), 2); assert_eq!(result.series[0].samples[0].timestamp, 2000);
421 assert_eq!(result.series[0].samples[1].timestamp, 3000);
422
423 let result = engine.query("cpu_usage", 5000, 6000).expect("valid query");
425 assert_eq!(result.series.len(), 0); }
427
428 #[test]
430 fn test_complex_query() {
431 let storage = Arc::new(MemoryStorage::new());
432 let engine = SimpleQueryEngine::new(storage.clone());
433
434 storage.add_series({
436 let mut ts = TimeSeries::new(vec![
437 Label::new("__name__", "http_requests"),
438 Label::new("job", "api"),
439 Label::new("method", "GET"),
440 ]);
441 ts.add_sample(Sample::new(1000, 10.0));
442 ts
443 });
444
445 storage.add_series({
446 let mut ts = TimeSeries::new(vec![
447 Label::new("__name__", "http_requests"),
448 Label::new("job", "api"),
449 Label::new("method", "POST"),
450 ]);
451 ts.add_sample(Sample::new(1000, 5.0));
452 ts
453 });
454
455 storage.add_series({
456 let mut ts = TimeSeries::new(vec![
457 Label::new("__name__", "http_requests"),
458 Label::new("job", "web"),
459 Label::new("method", "GET"),
460 ]);
461 ts.add_sample(Sample::new(1000, 15.0));
462 ts
463 });
464
465 let result = engine
467 .query(r#"http_requests{job="api",method!="POST"}"#, 0, 2000)
468 .expect("valid query");
469 assert_eq!(result.series.len(), 1); assert_eq!(result.series[0].samples[0].value, 10.0);
471
472 let result =
474 engine.query(r#"http_requests{job=~".*api.*"}"#, 0, 2000).expect("valid query");
475 assert_eq!(result.series.len(), 2); }
477}