rusty_promql_parser/parser/
subquery.rs1use nom::{
37 IResult, Parser,
38 character::complete::char,
39 combinator::{map, opt, peek, recognize},
40 sequence::delimited,
41};
42
43use crate::ast::{Expr, SubqueryExpr};
44use crate::lexer::duration::{Duration, duration};
45use crate::parser::selector::parse_modifiers;
46
47pub fn subquery_range(input: &str) -> IResult<&str, (Duration, Option<Duration>)> {
67 delimited(
68 char('['),
69 map((duration, char(':'), opt(duration)), |(range, _, step)| {
70 (range, step)
71 }),
72 char(']'),
73 )
74 .parse(input)
75}
76
77#[allow(dead_code)]
85pub(crate) fn try_parse_subquery(input: &str, expr: Expr) -> IResult<&str, SubqueryExpr> {
86 map(
87 (subquery_range, parse_modifiers),
88 move |((range, step), (at, offset))| SubqueryExpr {
89 expr: expr.clone(),
90 range,
91 step,
92 offset,
93 at,
94 },
95 )
96 .parse(input)
97}
98
99pub(crate) fn looks_like_subquery(input: &str) -> bool {
105 peek(recognize((char('['), duration, char(':'))))
107 .parse(input)
108 .is_ok()
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114 use crate::parser::selector::VectorSelector;
115
116 #[test]
117 fn test_subquery_range_with_step() {
118 let (rest, (range, step)) = subquery_range("[5m:1m]").unwrap();
119 assert!(rest.is_empty());
120 assert_eq!(range.as_millis(), 5 * 60 * 1000);
121 assert_eq!(step.unwrap().as_millis(), 60 * 1000);
122 }
123
124 #[test]
125 fn test_subquery_range_without_step() {
126 let (rest, (range, step)) = subquery_range("[30m:]").unwrap();
127 assert!(rest.is_empty());
128 assert_eq!(range.as_millis(), 30 * 60 * 1000);
129 assert!(step.is_none());
130 }
131
132 #[test]
133 fn test_subquery_range_various_durations() {
134 let (_, (range, step)) = subquery_range("[1h:5m]").unwrap();
135 assert_eq!(range.as_millis(), 60 * 60 * 1000);
136 assert_eq!(step.unwrap().as_millis(), 5 * 60 * 1000);
137
138 let (_, (range, step)) = subquery_range("[1d:1h]").unwrap();
139 assert_eq!(range.as_millis(), 24 * 60 * 60 * 1000);
140 assert_eq!(step.unwrap().as_millis(), 60 * 60 * 1000);
141
142 let (_, (range, step)) = subquery_range("[1w:]").unwrap();
143 assert_eq!(range.as_millis(), 7 * 24 * 60 * 60 * 1000);
144 assert!(step.is_none());
145 }
146
147 #[test]
148 fn test_subquery_range_compound_duration() {
149 let (_, (range, step)) = subquery_range("[1h30m:5m30s]").unwrap();
150 assert_eq!(range.as_millis(), (60 + 30) * 60 * 1000);
151 assert_eq!(step.unwrap().as_millis(), (5 * 60 + 30) * 1000);
152 }
153
154 #[test]
155 fn test_subquery_range_invalid_no_colon() {
156 assert!(subquery_range("[5m]").is_err());
158 }
159
160 #[test]
161 fn test_subquery_range_invalid_empty() {
162 assert!(subquery_range("[]").is_err());
163 assert!(subquery_range("[:]").is_err());
164 }
165
166 #[test]
167 fn test_try_parse_subquery() {
168 let expr = Expr::VectorSelector(VectorSelector::new("metric"));
169 let (rest, sq) = try_parse_subquery("[5m:1m]", expr).unwrap();
170 assert!(rest.is_empty());
171 assert_eq!(sq.range.as_millis(), 5 * 60 * 1000);
172 assert_eq!(sq.step.unwrap().as_millis(), 60 * 1000);
173 }
174
175 #[test]
176 fn test_try_parse_subquery_with_offset() {
177 let expr = Expr::VectorSelector(VectorSelector::new("metric"));
178 let (rest, sq) = try_parse_subquery("[5m:1m] offset 10m", expr).unwrap();
179 assert!(rest.is_empty());
180 assert_eq!(sq.offset.unwrap().as_millis(), 10 * 60 * 1000);
181 }
182
183 #[test]
184 fn test_try_parse_subquery_with_at() {
185 let expr = Expr::VectorSelector(VectorSelector::new("metric"));
186 let (rest, sq) = try_parse_subquery("[5m:1m] @ 1609459200", expr).unwrap();
187 assert!(rest.is_empty());
188 assert!(sq.at.is_some());
189 }
190
191 #[test]
192 fn test_try_parse_subquery_with_both_modifiers() {
193 let expr = Expr::VectorSelector(VectorSelector::new("metric"));
194 let (rest, sq) = try_parse_subquery("[5m:1m] @ 1609459200 offset 10m", expr).unwrap();
195 assert!(rest.is_empty());
196 assert!(sq.at.is_some());
197 assert!(sq.offset.is_some());
198
199 let expr = Expr::VectorSelector(VectorSelector::new("metric"));
201 let (rest, sq) = try_parse_subquery("[5m:1m] offset 10m @ 1609459200", expr).unwrap();
202 assert!(rest.is_empty());
203 assert!(sq.at.is_some());
204 assert!(sq.offset.is_some());
205 }
206
207 #[test]
208 fn test_looks_like_subquery() {
209 assert!(looks_like_subquery("[5m:]"));
211 assert!(looks_like_subquery("[5m:1m]"));
212 assert!(looks_like_subquery("[1h30m:5m]"));
213
214 assert!(!looks_like_subquery("[5m]"));
216 assert!(!looks_like_subquery("[1h]"));
217
218 assert!(!looks_like_subquery("foo"));
220 assert!(!looks_like_subquery(""));
221 assert!(!looks_like_subquery("(5m)"));
222 }
223
224 #[test]
225 fn test_subquery_expr_display() {
226 let sq = SubqueryExpr {
227 expr: Expr::VectorSelector(VectorSelector::new("metric")),
228 range: Duration::from_secs(300),
229 step: Some(Duration::from_secs(60)),
230 offset: None,
231 at: None,
232 };
233 assert_eq!(sq.to_string(), "metric[5m:1m]");
234
235 let sq = SubqueryExpr {
236 expr: Expr::VectorSelector(VectorSelector::new("metric")),
237 range: Duration::from_secs(300),
238 step: None,
239 offset: None,
240 at: None,
241 };
242 assert_eq!(sq.to_string(), "metric[5m:]");
243
244 let sq = SubqueryExpr {
245 expr: Expr::VectorSelector(VectorSelector::new("metric")),
246 range: Duration::from_secs(300),
247 step: Some(Duration::from_secs(60)),
248 offset: Some(Duration::from_secs(600)),
249 at: None,
250 };
251 assert_eq!(sq.to_string(), "metric[5m:1m] offset 10m");
252 }
253}