use nom::{
IResult, Parser,
character::complete::char,
combinator::{map, opt, peek, recognize},
sequence::delimited,
};
use crate::ast::{Expr, SubqueryExpr};
use crate::lexer::duration::{Duration, duration};
use crate::parser::selector::parse_modifiers;
pub fn subquery_range(input: &str) -> IResult<&str, (Duration, Option<Duration>)> {
delimited(
char('['),
map((duration, char(':'), opt(duration)), |(range, _, step)| {
(range, step)
}),
char(']'),
)
.parse(input)
}
#[allow(dead_code)]
pub(crate) fn try_parse_subquery(input: &str, expr: Expr) -> IResult<&str, SubqueryExpr> {
map(
(subquery_range, parse_modifiers),
move |((range, step), (at, offset))| SubqueryExpr {
expr: expr.clone(),
range,
step,
offset,
at,
},
)
.parse(input)
}
pub(crate) fn looks_like_subquery(input: &str) -> bool {
peek(recognize((char('['), duration, char(':'))))
.parse(input)
.is_ok()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::parser::selector::VectorSelector;
#[test]
fn test_subquery_range_with_step() {
let (rest, (range, step)) = subquery_range("[5m:1m]").unwrap();
assert!(rest.is_empty());
assert_eq!(range.as_millis(), 5 * 60 * 1000);
assert_eq!(step.unwrap().as_millis(), 60 * 1000);
}
#[test]
fn test_subquery_range_without_step() {
let (rest, (range, step)) = subquery_range("[30m:]").unwrap();
assert!(rest.is_empty());
assert_eq!(range.as_millis(), 30 * 60 * 1000);
assert!(step.is_none());
}
#[test]
fn test_subquery_range_various_durations() {
let (_, (range, step)) = subquery_range("[1h:5m]").unwrap();
assert_eq!(range.as_millis(), 60 * 60 * 1000);
assert_eq!(step.unwrap().as_millis(), 5 * 60 * 1000);
let (_, (range, step)) = subquery_range("[1d:1h]").unwrap();
assert_eq!(range.as_millis(), 24 * 60 * 60 * 1000);
assert_eq!(step.unwrap().as_millis(), 60 * 60 * 1000);
let (_, (range, step)) = subquery_range("[1w:]").unwrap();
assert_eq!(range.as_millis(), 7 * 24 * 60 * 60 * 1000);
assert!(step.is_none());
}
#[test]
fn test_subquery_range_compound_duration() {
let (_, (range, step)) = subquery_range("[1h30m:5m30s]").unwrap();
assert_eq!(range.as_millis(), (60 + 30) * 60 * 1000);
assert_eq!(step.unwrap().as_millis(), (5 * 60 + 30) * 1000);
}
#[test]
fn test_subquery_range_invalid_no_colon() {
assert!(subquery_range("[5m]").is_err());
}
#[test]
fn test_subquery_range_invalid_empty() {
assert!(subquery_range("[]").is_err());
assert!(subquery_range("[:]").is_err());
}
#[test]
fn test_try_parse_subquery() {
let expr = Expr::VectorSelector(VectorSelector::new("metric"));
let (rest, sq) = try_parse_subquery("[5m:1m]", expr).unwrap();
assert!(rest.is_empty());
assert_eq!(sq.range.as_millis(), 5 * 60 * 1000);
assert_eq!(sq.step.unwrap().as_millis(), 60 * 1000);
}
#[test]
fn test_try_parse_subquery_with_offset() {
let expr = Expr::VectorSelector(VectorSelector::new("metric"));
let (rest, sq) = try_parse_subquery("[5m:1m] offset 10m", expr).unwrap();
assert!(rest.is_empty());
assert_eq!(sq.offset.unwrap().as_millis(), 10 * 60 * 1000);
}
#[test]
fn test_try_parse_subquery_with_at() {
let expr = Expr::VectorSelector(VectorSelector::new("metric"));
let (rest, sq) = try_parse_subquery("[5m:1m] @ 1609459200", expr).unwrap();
assert!(rest.is_empty());
assert!(sq.at.is_some());
}
#[test]
fn test_try_parse_subquery_with_both_modifiers() {
let expr = Expr::VectorSelector(VectorSelector::new("metric"));
let (rest, sq) = try_parse_subquery("[5m:1m] @ 1609459200 offset 10m", expr).unwrap();
assert!(rest.is_empty());
assert!(sq.at.is_some());
assert!(sq.offset.is_some());
let expr = Expr::VectorSelector(VectorSelector::new("metric"));
let (rest, sq) = try_parse_subquery("[5m:1m] offset 10m @ 1609459200", expr).unwrap();
assert!(rest.is_empty());
assert!(sq.at.is_some());
assert!(sq.offset.is_some());
}
#[test]
fn test_looks_like_subquery() {
assert!(looks_like_subquery("[5m:]"));
assert!(looks_like_subquery("[5m:1m]"));
assert!(looks_like_subquery("[1h30m:5m]"));
assert!(!looks_like_subquery("[5m]"));
assert!(!looks_like_subquery("[1h]"));
assert!(!looks_like_subquery("foo"));
assert!(!looks_like_subquery(""));
assert!(!looks_like_subquery("(5m)"));
}
#[test]
fn test_subquery_expr_display() {
let sq = SubqueryExpr {
expr: Expr::VectorSelector(VectorSelector::new("metric")),
range: Duration::from_secs(300),
step: Some(Duration::from_secs(60)),
offset: None,
at: None,
};
assert_eq!(sq.to_string(), "metric[5m:1m]");
let sq = SubqueryExpr {
expr: Expr::VectorSelector(VectorSelector::new("metric")),
range: Duration::from_secs(300),
step: None,
offset: None,
at: None,
};
assert_eq!(sq.to_string(), "metric[5m:]");
let sq = SubqueryExpr {
expr: Expr::VectorSelector(VectorSelector::new("metric")),
range: Duration::from_secs(300),
step: Some(Duration::from_secs(60)),
offset: Some(Duration::from_secs(600)),
at: None,
};
assert_eq!(sq.to_string(), "metric[5m:1m] offset 10m");
}
}