1use nom::{
61 IResult, Parser,
62 branch::alt,
63 bytes::complete::tag,
64 character::complete::char,
65 combinator::{map, opt},
66 multi::separated_list0,
67 sequence::{delimited, terminated},
68};
69
70use crate::lexer::{
71 duration::{Duration, duration, signed_duration},
72 identifier::{label_name, metric_name},
73 number::number,
74 string::string_literal,
75 whitespace::ws_opt,
76};
77
78#[derive(Debug, Clone, PartialEq)]
89pub enum AtModifier {
90 Timestamp(i64),
92 Start,
94 End,
96}
97
98impl std::fmt::Display for AtModifier {
99 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
100 match self {
101 AtModifier::Timestamp(ts) => {
102 let secs = *ts as f64 / 1000.0;
104 write!(f, "@ {:.3}", secs)
105 }
106 AtModifier::Start => write!(f, "@ start()"),
107 AtModifier::End => write!(f, "@ end()"),
108 }
109 }
110}
111
112#[derive(Debug, Clone, Copy, PartialEq, Eq)]
116pub enum LabelMatchOp {
117 Equal,
119 NotEqual,
121 RegexMatch,
123 RegexNotMatch,
125}
126
127impl LabelMatchOp {
128 pub fn as_str(&self) -> &'static str {
130 match self {
131 LabelMatchOp::Equal => "=",
132 LabelMatchOp::NotEqual => "!=",
133 LabelMatchOp::RegexMatch => "=~",
134 LabelMatchOp::RegexNotMatch => "!~",
135 }
136 }
137
138 pub fn is_negative(&self) -> bool {
140 matches!(self, LabelMatchOp::NotEqual | LabelMatchOp::RegexNotMatch)
141 }
142
143 pub fn is_regex(&self) -> bool {
145 matches!(self, LabelMatchOp::RegexMatch | LabelMatchOp::RegexNotMatch)
146 }
147}
148
149impl std::fmt::Display for LabelMatchOp {
150 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
151 write!(f, "{}", self.as_str())
152 }
153}
154
155#[derive(Debug, Clone, PartialEq, Eq)]
168pub struct LabelMatcher {
169 pub name: String,
171 pub op: LabelMatchOp,
173 pub value: String,
175}
176
177impl LabelMatcher {
178 pub fn new(name: impl Into<String>, op: LabelMatchOp, value: impl Into<String>) -> Self {
180 Self {
181 name: name.into(),
182 op,
183 value: value.into(),
184 }
185 }
186
187 pub fn matches_empty(&self) -> bool {
189 match self.op {
190 LabelMatchOp::Equal => self.value.is_empty(),
191 LabelMatchOp::NotEqual => !self.value.is_empty(),
192 LabelMatchOp::RegexMatch => {
193 self.value.is_empty()
196 || self.value == ".*"
197 || self.value == "^$"
198 || self.value == "^.*$"
199 }
200 LabelMatchOp::RegexNotMatch => {
201 self.value == ".+"
204 }
205 }
206 }
207}
208
209impl std::fmt::Display for LabelMatcher {
210 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
211 write!(
212 f,
213 "{}{}\"{}\"",
214 self.name,
215 self.op,
216 self.value.escape_default()
217 )
218 }
219}
220
221#[derive(Debug, Clone, PartialEq)]
236pub struct VectorSelector {
237 pub name: Option<String>,
239 pub matchers: Vec<LabelMatcher>,
241 pub offset: Option<Duration>,
243 pub at: Option<AtModifier>,
245}
246
247impl VectorSelector {
248 pub fn new(name: impl Into<String>) -> Self {
250 Self {
251 name: Some(name.into()),
252 matchers: Vec::new(),
253 offset: None,
254 at: None,
255 }
256 }
257
258 pub fn with_matchers(matchers: Vec<LabelMatcher>) -> Self {
260 Self {
261 name: None,
262 matchers,
263 offset: None,
264 at: None,
265 }
266 }
267
268 pub fn add_matcher(&mut self, matcher: LabelMatcher) {
270 self.matchers.push(matcher);
271 }
272
273 pub fn all_matchers(&self) -> Vec<LabelMatcher> {
275 let mut result = self.matchers.clone();
276 if let Some(ref name) = self.name {
277 result.push(LabelMatcher::new(
278 "__name__",
279 LabelMatchOp::Equal,
280 name.clone(),
281 ));
282 }
283 result
284 }
285
286 pub fn has_non_empty_matcher(&self) -> bool {
289 if self.name.is_some() {
291 return true;
292 }
293
294 self.matchers.iter().any(|m| !m.matches_empty())
296 }
297}
298
299impl std::fmt::Display for VectorSelector {
300 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
301 if let Some(ref name) = self.name {
302 write!(f, "{}", name)?;
303 }
304 if !self.matchers.is_empty() {
305 write!(f, "{{")?;
306 for (i, m) in self.matchers.iter().enumerate() {
307 if i > 0 {
308 write!(f, ", ")?;
309 }
310 write!(f, "{}", m)?;
311 }
312 write!(f, "}}")?;
313 }
314 if let Some(ref at) = self.at {
316 write!(f, " {}", at)?;
317 }
318 if let Some(ref offset) = self.offset {
319 write!(f, " offset {}", offset)?;
320 }
321 Ok(())
322 }
323}
324
325#[derive(Debug, Clone, PartialEq)]
340pub struct MatrixSelector {
341 pub selector: VectorSelector,
343 pub range: Duration,
345}
346
347impl MatrixSelector {
348 pub fn new(selector: VectorSelector, range: Duration) -> Self {
350 Self { selector, range }
351 }
352
353 pub fn with_name(name: impl Into<String>, range: Duration) -> Self {
355 Self {
356 selector: VectorSelector::new(name),
357 range,
358 }
359 }
360
361 pub fn name(&self) -> Option<&str> {
363 self.selector.name.as_deref()
364 }
365
366 pub fn matchers(&self) -> &[LabelMatcher] {
368 &self.selector.matchers
369 }
370
371 pub fn range_millis(&self) -> i64 {
373 self.range.as_millis()
374 }
375
376 pub fn offset(&self) -> Option<&Duration> {
378 self.selector.offset.as_ref()
379 }
380
381 pub fn offset_millis(&self) -> Option<i64> {
383 self.selector.offset.map(|d| d.as_millis())
384 }
385
386 pub fn at(&self) -> Option<&AtModifier> {
388 self.selector.at.as_ref()
389 }
390}
391
392impl std::fmt::Display for MatrixSelector {
393 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
394 if let Some(ref name) = self.selector.name {
396 write!(f, "{}", name)?;
397 }
398 if !self.selector.matchers.is_empty() {
399 write!(f, "{{")?;
400 for (i, m) in self.selector.matchers.iter().enumerate() {
401 if i > 0 {
402 write!(f, ", ")?;
403 }
404 write!(f, "{}", m)?;
405 }
406 write!(f, "}}")?;
407 }
408 write!(f, "[{}]", self.range)?;
410 if let Some(ref at) = self.selector.at {
412 write!(f, " {}", at)?;
413 }
414 if let Some(ref offset) = self.selector.offset {
416 write!(f, " offset {}", offset)?;
417 }
418 Ok(())
419 }
420}
421
422fn range_duration(input: &str) -> IResult<&str, Duration> {
424 delimited(char('['), duration, char(']')).parse(input)
425}
426
427fn offset_keyword(input: &str) -> IResult<&str, &str> {
429 alt((tag("offset"), tag("OFFSET"), tag("Offset"))).parse(input)
430}
431
432pub fn offset_modifier(input: &str) -> IResult<&str, Duration> {
455 let (rest, _) = ws_opt(input)?;
456 let (rest, _) = offset_keyword(rest)?;
457 let (rest, _) = ws_opt(rest)?;
458 signed_duration(rest)
459}
460
461pub fn at_modifier(input: &str) -> IResult<&str, AtModifier> {
484 let (rest, _) = ws_opt(input)?;
485 let (rest, _) = char('@')(rest)?;
486 let (rest, _) = ws_opt(rest)?;
487
488 if let Ok((rest, _)) = tag::<&str, &str, nom::error::Error<&str>>("start()")(rest) {
490 return Ok((rest, AtModifier::Start));
491 }
492 if let Ok((rest, _)) = tag::<&str, &str, nom::error::Error<&str>>("end()")(rest) {
493 return Ok((rest, AtModifier::End));
494 }
495
496 let (rest, ts) = number(rest)?;
498
499 if ts.is_infinite() || ts.is_nan() {
501 return Err(nom::Err::Error(nom::error::Error::new(
502 input,
503 nom::error::ErrorKind::Verify,
504 )));
505 }
506
507 let ts_ms = (ts * 1000.0).round() as i64;
509 Ok((rest, AtModifier::Timestamp(ts_ms)))
510}
511
512pub fn matrix_selector(input: &str) -> IResult<&str, MatrixSelector> {
534 map(
535 (base_vector_selector, range_duration, parse_modifiers),
536 |(mut selector, range, (at, offset))| {
537 selector.at = at;
538 selector.offset = offset;
539 MatrixSelector::new(selector, range)
540 },
541 )
542 .parse(input)
543}
544
545pub(crate) fn parse_modifiers(
548 input: &str,
549) -> IResult<&str, (Option<AtModifier>, Option<Duration>)> {
550 let mut rest = input;
551 let mut at = None;
552 let mut offset = None;
553
554 loop {
555 if let Ok((next, parsed_at)) = at_modifier(rest) {
556 if at.is_some() {
557 return Err(nom::Err::Error(nom::error::Error::new(
558 rest,
559 nom::error::ErrorKind::Verify,
560 )));
561 }
562 at = Some(parsed_at);
563 rest = next;
564 continue;
565 }
566
567 if let Ok((next, parsed_offset)) = offset_modifier(rest) {
568 if offset.is_some() {
569 return Err(nom::Err::Error(nom::error::Error::new(
570 rest,
571 nom::error::ErrorKind::Verify,
572 )));
573 }
574 offset = Some(parsed_offset);
575 rest = next;
576 continue;
577 }
578
579 break;
580 }
581
582 Ok((rest, (at, offset)))
583}
584
585fn label_match_op(input: &str) -> IResult<&str, LabelMatchOp> {
587 alt((
588 map(tag("!="), |_| LabelMatchOp::NotEqual),
589 map(tag("!~"), |_| LabelMatchOp::RegexNotMatch),
590 map(tag("=~"), |_| LabelMatchOp::RegexMatch),
591 map(tag("="), |_| LabelMatchOp::Equal),
592 ))
593 .parse(input)
594}
595
596fn label_matcher(input: &str) -> IResult<&str, LabelMatcher> {
598 map(
599 (
600 ws_opt,
601 label_name,
602 ws_opt,
603 label_match_op,
604 ws_opt,
605 string_literal,
606 ),
607 |(_, name, _, op, _, value)| LabelMatcher::new(name.to_string(), op, value),
608 )
609 .parse(input)
610}
611
612fn quoted_metric_matcher(input: &str) -> IResult<&str, LabelMatcher> {
614 map((ws_opt, string_literal), |(_, name)| {
615 LabelMatcher::new("__name__", LabelMatchOp::Equal, name)
616 })
617 .parse(input)
618}
619
620fn matcher_item(input: &str) -> IResult<&str, LabelMatcher> {
622 alt((label_matcher, quoted_metric_matcher)).parse(input)
623}
624
625pub fn label_matchers(input: &str) -> IResult<&str, Vec<LabelMatcher>> {
627 delimited(
628 (char('{'), ws_opt),
629 terminated(
630 separated_list0(delimited(ws_opt, char(','), ws_opt), matcher_item),
631 opt((ws_opt, char(','))), ),
633 (ws_opt, char('}')),
634 )
635 .parse(input)
636}
637
638pub fn vector_selector(input: &str) -> IResult<&str, VectorSelector> {
659 map(
660 (base_vector_selector, parse_modifiers),
661 |(mut selector, (at, offset))| {
662 selector.at = at;
663 selector.offset = offset;
664 selector
665 },
666 )
667 .parse(input)
668}
669
670pub fn base_vector_selector(input: &str) -> IResult<&str, VectorSelector> {
673 let name_result = metric_name(input);
675
676 match name_result {
677 Ok((rest, name)) => {
678 let (rest, matchers) = opt(label_matchers).parse(rest)?;
680 Ok((
681 rest,
682 VectorSelector {
683 name: Some(name.to_string()),
684 matchers: matchers.unwrap_or_default(),
685 offset: None,
686 at: None,
687 },
688 ))
689 }
690 Err(_) => {
691 let (rest, matchers) = label_matchers(input)?;
693
694 let name = matchers
696 .iter()
697 .find(|m| m.name == "__name__" && m.op == LabelMatchOp::Equal)
698 .map(|m| m.value.clone());
699
700 let other_matchers: Vec<_> = if name.is_some() {
702 matchers
703 .into_iter()
704 .filter(|m| !(m.name == "__name__" && m.op == LabelMatchOp::Equal))
705 .collect()
706 } else {
707 matchers
708 };
709
710 Ok((
711 rest,
712 VectorSelector {
713 name,
714 matchers: other_matchers,
715 offset: None,
716 at: None,
717 },
718 ))
719 }
720 }
721}
722
723#[cfg(test)]
724mod tests {
725 use super::*;
726
727 #[test]
729 fn test_label_match_op_parse() {
730 assert_eq!(label_match_op("=").unwrap().1, LabelMatchOp::Equal);
731 assert_eq!(label_match_op("!=").unwrap().1, LabelMatchOp::NotEqual);
732 assert_eq!(label_match_op("=~").unwrap().1, LabelMatchOp::RegexMatch);
733 assert_eq!(label_match_op("!~").unwrap().1, LabelMatchOp::RegexNotMatch);
734 }
735
736 #[test]
737 fn test_label_match_op_display() {
738 assert_eq!(LabelMatchOp::Equal.to_string(), "=");
739 assert_eq!(LabelMatchOp::NotEqual.to_string(), "!=");
740 assert_eq!(LabelMatchOp::RegexMatch.to_string(), "=~");
741 assert_eq!(LabelMatchOp::RegexNotMatch.to_string(), "!~");
742 }
743
744 #[test]
745 fn test_label_match_op_properties() {
746 assert!(!LabelMatchOp::Equal.is_negative());
747 assert!(LabelMatchOp::NotEqual.is_negative());
748 assert!(!LabelMatchOp::RegexMatch.is_negative());
749 assert!(LabelMatchOp::RegexNotMatch.is_negative());
750
751 assert!(!LabelMatchOp::Equal.is_regex());
752 assert!(!LabelMatchOp::NotEqual.is_regex());
753 assert!(LabelMatchOp::RegexMatch.is_regex());
754 assert!(LabelMatchOp::RegexNotMatch.is_regex());
755 }
756
757 #[test]
759 fn test_label_matcher_parse() {
760 let (rest, m) = label_matcher(r#"job="prometheus""#).unwrap();
761 assert!(rest.is_empty());
762 assert_eq!(m.name, "job");
763 assert_eq!(m.op, LabelMatchOp::Equal);
764 assert_eq!(m.value, "prometheus");
765 }
766
767 #[test]
768 fn test_label_matcher_parse_with_spaces() {
769 let (rest, m) = label_matcher(r#" job = "prometheus" "#).unwrap();
770 assert_eq!(rest, " "); assert_eq!(m.name, "job");
772 assert_eq!(m.value, "prometheus");
773 }
774
775 #[test]
776 fn test_label_matcher_not_equal() {
777 let (_, m) = label_matcher(r#"env!="prod""#).unwrap();
778 assert_eq!(m.op, LabelMatchOp::NotEqual);
779 }
780
781 #[test]
782 fn test_label_matcher_regex() {
783 let (_, m) = label_matcher(r#"path=~"/api/.*""#).unwrap();
784 assert_eq!(m.op, LabelMatchOp::RegexMatch);
785 assert_eq!(m.value, "/api/.*");
786 }
787
788 #[test]
789 fn test_label_matcher_regex_not() {
790 let (_, m) = label_matcher(r#"status!~"5..""#).unwrap();
791 assert_eq!(m.op, LabelMatchOp::RegexNotMatch);
792 }
793
794 #[test]
795 fn test_label_matcher_matches_empty() {
796 assert!(LabelMatcher::new("a", LabelMatchOp::Equal, "").matches_empty());
798 assert!(!LabelMatcher::new("a", LabelMatchOp::Equal, "foo").matches_empty());
800 assert!(!LabelMatcher::new("a", LabelMatchOp::NotEqual, "").matches_empty());
802 assert!(LabelMatcher::new("a", LabelMatchOp::NotEqual, "foo").matches_empty());
804 assert!(LabelMatcher::new("a", LabelMatchOp::RegexMatch, ".*").matches_empty());
806 assert!(!LabelMatcher::new("a", LabelMatchOp::RegexMatch, ".+").matches_empty());
808 assert!(LabelMatcher::new("a", LabelMatchOp::RegexNotMatch, ".+").matches_empty());
810 }
811
812 #[test]
814 fn test_vector_selector_simple_name() {
815 let (rest, sel) = vector_selector("foo").unwrap();
816 assert!(rest.is_empty());
817 assert_eq!(sel.name, Some("foo".to_string()));
818 assert!(sel.matchers.is_empty());
819 }
820
821 #[test]
822 fn test_vector_selector_with_underscore() {
823 let (rest, sel) = vector_selector("http_requests_total").unwrap();
824 assert!(rest.is_empty());
825 assert_eq!(sel.name, Some("http_requests_total".to_string()));
826 }
827
828 #[test]
829 fn test_vector_selector_with_colon() {
830 let (rest, sel) = vector_selector("foo:bar:baz").unwrap();
831 assert!(rest.is_empty());
832 assert_eq!(sel.name, Some("foo:bar:baz".to_string()));
833 }
834
835 #[test]
836 fn test_vector_selector_with_label() {
837 let (rest, sel) = vector_selector(r#"foo{bar="baz"}"#).unwrap();
838 assert!(rest.is_empty());
839 assert_eq!(sel.name, Some("foo".to_string()));
840 assert_eq!(sel.matchers.len(), 1);
841 assert_eq!(sel.matchers[0].name, "bar");
842 assert_eq!(sel.matchers[0].value, "baz");
843 }
844
845 #[test]
846 fn test_vector_selector_multiple_labels() {
847 let (rest, sel) = vector_selector(r#"foo{a="b", c="d"}"#).unwrap();
848 assert!(rest.is_empty());
849 assert_eq!(sel.name, Some("foo".to_string()));
850 assert_eq!(sel.matchers.len(), 2);
851 assert_eq!(sel.matchers[0].name, "a");
852 assert_eq!(sel.matchers[1].name, "c");
853 }
854
855 #[test]
856 fn test_vector_selector_trailing_comma() {
857 let (rest, sel) = vector_selector(r#"foo{a="b",}"#).unwrap();
858 assert!(rest.is_empty());
859 assert_eq!(sel.matchers.len(), 1);
860 }
861
862 #[test]
863 fn test_vector_selector_labels_only() {
864 let (rest, sel) = vector_selector(r#"{job="prometheus"}"#).unwrap();
865 assert!(rest.is_empty());
866 assert!(sel.name.is_none());
867 assert_eq!(sel.matchers.len(), 1);
868 assert_eq!(sel.matchers[0].name, "job");
869 }
870
871 #[test]
872 fn test_vector_selector_quoted_metric_name() {
873 let (rest, sel) = vector_selector(r#"{"foo"}"#).unwrap();
874 assert!(rest.is_empty());
875 assert_eq!(sel.name, Some("foo".to_string()));
876 assert!(sel.matchers.is_empty());
877 }
878
879 #[test]
880 fn test_vector_selector_quoted_metric_with_labels() {
881 let (rest, sel) = vector_selector(r#"{"foo", bar="baz"}"#).unwrap();
882 assert!(rest.is_empty());
883 assert_eq!(sel.name, Some("foo".to_string()));
884 assert_eq!(sel.matchers.len(), 1);
885 }
886
887 #[test]
888 fn test_vector_selector_all_operators() {
889 let (rest, sel) = vector_selector(r#"foo{a="b", c!="d", e=~"f", g!~"h"}"#).unwrap();
890 assert!(rest.is_empty());
891 assert_eq!(sel.matchers.len(), 4);
892 assert_eq!(sel.matchers[0].op, LabelMatchOp::Equal);
893 assert_eq!(sel.matchers[1].op, LabelMatchOp::NotEqual);
894 assert_eq!(sel.matchers[2].op, LabelMatchOp::RegexMatch);
895 assert_eq!(sel.matchers[3].op, LabelMatchOp::RegexNotMatch);
896 }
897
898 #[test]
899 fn test_vector_selector_has_non_empty_matcher() {
900 let sel = VectorSelector::new("foo");
902 assert!(sel.has_non_empty_matcher());
903
904 let mut sel = VectorSelector::with_matchers(vec![]);
906 sel.add_matcher(LabelMatcher::new("job", LabelMatchOp::Equal, "test"));
907 assert!(sel.has_non_empty_matcher());
908
909 let sel =
911 VectorSelector::with_matchers(vec![LabelMatcher::new("x", LabelMatchOp::Equal, "")]);
912 assert!(!sel.has_non_empty_matcher());
913 }
914
915 #[test]
916 fn test_vector_selector_display() {
917 let mut sel = VectorSelector::new("foo");
918 assert_eq!(sel.to_string(), "foo");
919
920 sel.add_matcher(LabelMatcher::new("bar", LabelMatchOp::Equal, "baz"));
921 assert_eq!(sel.to_string(), r#"foo{bar="baz"}"#);
922 }
923
924 #[test]
925 fn test_vector_selector_single_quoted() {
926 let (rest, sel) = vector_selector(r#"foo{bar='baz'}"#).unwrap();
927 assert!(rest.is_empty());
928 assert_eq!(sel.matchers[0].value, "baz");
929 }
930
931 #[test]
932 fn test_vector_selector_backtick() {
933 let (rest, sel) = vector_selector(r#"foo{bar=`baz`}"#).unwrap();
934 assert!(rest.is_empty());
935 assert_eq!(sel.matchers[0].value, "baz");
936 }
937
938 #[test]
939 fn test_vector_selector_keyword_as_metric() {
940 for keyword in [
942 "sum", "min", "max", "avg", "count", "offset", "by", "without",
943 ] {
944 let result = vector_selector(keyword);
945 assert!(
946 result.is_ok(),
947 "Failed to parse keyword as metric: {}",
948 keyword
949 );
950 let (_, sel) = result.unwrap();
951 assert_eq!(sel.name, Some(keyword.to_string()));
952 }
953 }
954
955 #[test]
956 fn test_vector_selector_empty_braces() {
957 let (rest, sel) = vector_selector("{}").unwrap();
959 assert!(rest.is_empty());
960 assert!(sel.name.is_none());
961 assert!(sel.matchers.is_empty());
962 }
964
965 #[test]
967 fn test_matrix_selector_simple() {
968 let (rest, sel) = matrix_selector("foo[5m]").unwrap();
969 assert!(rest.is_empty());
970 assert_eq!(sel.name(), Some("foo"));
971 assert_eq!(sel.range_millis(), 5 * 60 * 1000);
972 }
973
974 #[test]
975 fn test_matrix_selector_with_labels() {
976 let (rest, sel) = matrix_selector(r#"foo{bar="baz"}[5m]"#).unwrap();
977 assert!(rest.is_empty());
978 assert_eq!(sel.name(), Some("foo"));
979 assert_eq!(sel.matchers().len(), 1);
980 assert_eq!(sel.range_millis(), 5 * 60 * 1000);
981 }
982
983 #[test]
984 fn test_matrix_selector_various_durations() {
985 let (_, sel) = matrix_selector("foo[30s]").unwrap();
987 assert_eq!(sel.range_millis(), 30 * 1000);
988
989 let (_, sel) = matrix_selector("foo[5m]").unwrap();
991 assert_eq!(sel.range_millis(), 5 * 60 * 1000);
992
993 let (_, sel) = matrix_selector("foo[1h]").unwrap();
995 assert_eq!(sel.range_millis(), 60 * 60 * 1000);
996
997 let (_, sel) = matrix_selector("foo[1d]").unwrap();
999 assert_eq!(sel.range_millis(), 24 * 60 * 60 * 1000);
1000
1001 let (_, sel) = matrix_selector("foo[1w]").unwrap();
1003 assert_eq!(sel.range_millis(), 7 * 24 * 60 * 60 * 1000);
1004
1005 let (_, sel) = matrix_selector("foo[100ms]").unwrap();
1007 assert_eq!(sel.range_millis(), 100);
1008 }
1009
1010 #[test]
1011 fn test_matrix_selector_compound_duration() {
1012 let (rest, sel) = matrix_selector("foo[1h30m]").unwrap();
1014 assert!(rest.is_empty());
1015 assert_eq!(sel.range_millis(), (60 + 30) * 60 * 1000);
1016 }
1017
1018 #[test]
1019 fn test_matrix_selector_labels_only() {
1020 let (rest, sel) = matrix_selector(r#"{job="prometheus"}[5m]"#).unwrap();
1021 assert!(rest.is_empty());
1022 assert!(sel.name().is_none());
1023 assert_eq!(sel.matchers().len(), 1);
1024 }
1025
1026 #[test]
1027 fn test_matrix_selector_display() {
1028 let sel = MatrixSelector::with_name("foo", Duration::from_secs(300));
1029 assert_eq!(sel.to_string(), "foo[5m]");
1030 }
1031
1032 #[test]
1033 fn test_matrix_selector_display_with_labels() {
1034 let mut vs = VectorSelector::new("foo");
1035 vs.add_matcher(LabelMatcher::new("bar", LabelMatchOp::Equal, "baz"));
1036 let sel = MatrixSelector::new(vs, Duration::from_secs(300));
1037 assert_eq!(sel.to_string(), r#"foo{bar="baz"}[5m]"#);
1038 }
1039
1040 #[test]
1041 fn test_matrix_selector_no_range_fails() {
1042 let result = matrix_selector("foo");
1044 assert!(result.is_err());
1045 }
1046
1047 #[test]
1048 fn test_matrix_selector_empty_range_fails() {
1049 let result = matrix_selector("foo[]");
1050 assert!(result.is_err());
1051 }
1052
1053 #[test]
1055 fn test_offset_modifier_basic() {
1056 let (rest, dur) = offset_modifier(" offset 5m").unwrap();
1057 assert!(rest.is_empty());
1058 assert_eq!(dur.as_millis(), 5 * 60 * 1000);
1059 }
1060
1061 #[test]
1062 fn test_offset_modifier_negative() {
1063 let (rest, dur) = offset_modifier(" offset -7m").unwrap();
1064 assert!(rest.is_empty());
1065 assert_eq!(dur.as_millis(), -7 * 60 * 1000);
1066 }
1067
1068 #[test]
1069 fn test_offset_modifier_uppercase() {
1070 let (rest, dur) = offset_modifier(" OFFSET 1h30m").unwrap();
1071 assert!(rest.is_empty());
1072 assert_eq!(dur.as_millis(), 90 * 60 * 1000);
1073 }
1074
1075 #[test]
1076 fn test_offset_modifier_complex_duration() {
1077 let (rest, dur) = offset_modifier(" OFFSET 1m30ms").unwrap();
1078 assert!(rest.is_empty());
1079 assert_eq!(dur.as_millis(), 60 * 1000 + 30);
1080 }
1081
1082 #[test]
1083 fn test_vector_selector_with_offset() {
1084 let (rest, sel) = vector_selector("foo offset 5m").unwrap();
1085 assert!(rest.is_empty());
1086 assert_eq!(sel.name, Some("foo".to_string()));
1087 assert_eq!(sel.offset.unwrap().as_millis(), 5 * 60 * 1000);
1088 }
1089
1090 #[test]
1091 fn test_vector_selector_with_negative_offset() {
1092 let (rest, sel) = vector_selector("foo offset -7m").unwrap();
1093 assert!(rest.is_empty());
1094 assert_eq!(sel.name, Some("foo".to_string()));
1095 assert_eq!(sel.offset.unwrap().as_millis(), -7 * 60 * 1000);
1096 }
1097
1098 #[test]
1099 fn test_vector_selector_with_labels_and_offset() {
1100 let (rest, sel) = vector_selector(r#"foo{bar="baz"} offset 1h"#).unwrap();
1101 assert!(rest.is_empty());
1102 assert_eq!(sel.name, Some("foo".to_string()));
1103 assert_eq!(sel.matchers.len(), 1);
1104 assert_eq!(sel.offset.unwrap().as_millis(), 60 * 60 * 1000);
1105 }
1106
1107 #[test]
1108 fn test_vector_selector_display_with_offset() {
1109 let mut sel = VectorSelector::new("foo");
1110 sel.offset = Some(Duration::from_secs(300));
1111 assert_eq!(sel.to_string(), "foo offset 5m");
1112 }
1113
1114 #[test]
1115 fn test_matrix_selector_with_offset() {
1116 let (rest, sel) = matrix_selector("foo[5m] offset 1h").unwrap();
1117 assert!(rest.is_empty());
1118 assert_eq!(sel.name(), Some("foo"));
1119 assert_eq!(sel.range_millis(), 5 * 60 * 1000);
1120 assert_eq!(sel.offset_millis(), Some(60 * 60 * 1000));
1121 }
1122
1123 #[test]
1124 fn test_matrix_selector_with_labels_and_offset() {
1125 let (rest, sel) = matrix_selector(r#"foo{bar="baz"}[5m] offset 30m"#).unwrap();
1126 assert!(rest.is_empty());
1127 assert_eq!(sel.name(), Some("foo"));
1128 assert_eq!(sel.matchers().len(), 1);
1129 assert_eq!(sel.range_millis(), 5 * 60 * 1000);
1130 assert_eq!(sel.offset_millis(), Some(30 * 60 * 1000));
1131 }
1132
1133 #[test]
1134 fn test_matrix_selector_with_negative_offset() {
1135 let (rest, sel) = matrix_selector("foo[5m] offset -1h").unwrap();
1136 assert!(rest.is_empty());
1137 assert_eq!(sel.offset_millis(), Some(-60 * 60 * 1000));
1138 }
1139
1140 #[test]
1141 fn test_matrix_selector_display_with_offset() {
1142 let mut vs = VectorSelector::new("foo");
1143 vs.offset = Some(Duration::from_secs(3600));
1144 let sel = MatrixSelector::new(vs, Duration::from_secs(300));
1145 assert_eq!(sel.to_string(), "foo[5m] offset 1h");
1146 }
1147
1148 #[test]
1150 fn test_at_modifier_timestamp() {
1151 let (rest, at) = at_modifier(" @ 1603774568").unwrap();
1152 assert!(rest.is_empty());
1153 assert_eq!(at, AtModifier::Timestamp(1_603_774_568_000));
1154 }
1155
1156 #[test]
1157 fn test_at_modifier_negative_timestamp() {
1158 let (rest, at) = at_modifier(" @ -100").unwrap();
1159 assert!(rest.is_empty());
1160 assert_eq!(at, AtModifier::Timestamp(-100_000));
1161 }
1162
1163 #[test]
1164 fn test_at_modifier_float_timestamp() {
1165 let (rest, at) = at_modifier(" @ 3.33").unwrap();
1166 assert!(rest.is_empty());
1167 assert_eq!(at, AtModifier::Timestamp(3_330));
1168 }
1169
1170 #[test]
1171 fn test_at_modifier_start() {
1172 let (rest, at) = at_modifier(" @ start()").unwrap();
1173 assert!(rest.is_empty());
1174 assert_eq!(at, AtModifier::Start);
1175 }
1176
1177 #[test]
1178 fn test_at_modifier_end() {
1179 let (rest, at) = at_modifier(" @ end()").unwrap();
1180 assert!(rest.is_empty());
1181 assert_eq!(at, AtModifier::End);
1182 }
1183
1184 #[test]
1185 fn test_at_modifier_display_timestamp() {
1186 let at = AtModifier::Timestamp(1_603_774_568_000);
1187 assert_eq!(at.to_string(), "@ 1603774568.000");
1188 }
1189
1190 #[test]
1191 fn test_at_modifier_display_start() {
1192 assert_eq!(AtModifier::Start.to_string(), "@ start()");
1193 }
1194
1195 #[test]
1196 fn test_at_modifier_display_end() {
1197 assert_eq!(AtModifier::End.to_string(), "@ end()");
1198 }
1199
1200 #[test]
1201 fn test_vector_selector_with_at() {
1202 let (rest, sel) = vector_selector("foo @ 1603774568").unwrap();
1203 assert!(rest.is_empty());
1204 assert_eq!(sel.name, Some("foo".to_string()));
1205 assert_eq!(sel.at, Some(AtModifier::Timestamp(1_603_774_568_000)));
1206 }
1207
1208 #[test]
1209 fn test_vector_selector_with_at_start() {
1210 let (rest, sel) = vector_selector("foo @ start()").unwrap();
1211 assert!(rest.is_empty());
1212 assert_eq!(sel.at, Some(AtModifier::Start));
1213 }
1214
1215 #[test]
1216 fn test_vector_selector_with_at_and_offset() {
1217 let (rest, sel) = vector_selector("foo @ 123 offset 5m").unwrap();
1219 assert!(rest.is_empty());
1220 assert_eq!(sel.at, Some(AtModifier::Timestamp(123_000)));
1221 assert_eq!(sel.offset.unwrap().as_millis(), 5 * 60 * 1000);
1222 }
1223
1224 #[test]
1225 fn test_vector_selector_with_offset_and_at() {
1226 let (rest, sel) = vector_selector("foo offset 5m @ 123").unwrap();
1228 assert!(rest.is_empty());
1229 assert_eq!(sel.at, Some(AtModifier::Timestamp(123_000)));
1230 assert_eq!(sel.offset.unwrap().as_millis(), 5 * 60 * 1000);
1231 }
1232
1233 #[test]
1234 fn test_vector_selector_display_with_at() {
1235 let mut sel = VectorSelector::new("foo");
1236 sel.at = Some(AtModifier::Timestamp(123_000));
1237 assert_eq!(sel.to_string(), "foo @ 123.000");
1238 }
1239
1240 #[test]
1241 fn test_vector_selector_display_with_at_and_offset() {
1242 let mut sel = VectorSelector::new("foo");
1243 sel.at = Some(AtModifier::Start);
1244 sel.offset = Some(Duration::from_secs(300));
1245 assert_eq!(sel.to_string(), "foo @ start() offset 5m");
1246 }
1247
1248 #[test]
1249 fn test_matrix_selector_with_at() {
1250 let (rest, sel) = matrix_selector("foo[5m] @ 123").unwrap();
1251 assert!(rest.is_empty());
1252 assert_eq!(sel.at(), Some(&AtModifier::Timestamp(123_000)));
1253 }
1254
1255 #[test]
1256 fn test_matrix_selector_with_at_and_offset() {
1257 let (rest, sel) = matrix_selector("foo[5m] @ 123 offset 1h").unwrap();
1258 assert!(rest.is_empty());
1259 assert_eq!(sel.at(), Some(&AtModifier::Timestamp(123_000)));
1260 assert_eq!(sel.offset_millis(), Some(60 * 60 * 1000));
1261 }
1262
1263 #[test]
1264 fn test_matrix_selector_with_offset_and_at() {
1265 let (rest, sel) = matrix_selector("foo[5m] offset 1h @ 123").unwrap();
1266 assert!(rest.is_empty());
1267 assert_eq!(sel.at(), Some(&AtModifier::Timestamp(123_000)));
1268 assert_eq!(sel.offset_millis(), Some(60 * 60 * 1000));
1269 }
1270
1271 #[test]
1272 fn test_matrix_selector_display_with_at() {
1273 let mut vs = VectorSelector::new("foo");
1274 vs.at = Some(AtModifier::Start);
1275 let sel = MatrixSelector::new(vs, Duration::from_secs(300));
1276 assert_eq!(sel.to_string(), "foo[5m] @ start()");
1277 }
1278
1279 #[test]
1280 fn test_matrix_selector_display_with_at_and_offset() {
1281 let mut vs = VectorSelector::new("foo");
1282 vs.at = Some(AtModifier::Start);
1283 vs.offset = Some(Duration::from_secs(60));
1284 let sel = MatrixSelector::new(vs, Duration::from_secs(300));
1285 assert_eq!(sel.to_string(), "foo[5m] @ start() offset 1m");
1286 }
1287}