1use crate::prelude::*;
2use crate::BinOp;
3use crate::BoolModifier;
4use crate::CardType;
5use crate::Labels;
6use crate::LogExpr;
7use crate::MetricExpr;
8use crate::OnOrIgnoringModifier;
9use crate::Separator;
10
11#[derive(Debug, Clone, PartialEq)]
12pub enum Expr {
13 Metric(MetricExpr),
14 Log(LogExpr),
15 BinOp {
16 op: BinOp,
17 options: BinOpOptions,
18 lhs: Box<Expr>,
19 rhs: Box<Expr>,
20 },
21}
22
23impl Parser for Expr {
24 fn parse(input: &str) -> IResult<&str, Self> {
25 map(
26 tuple((
27 alt((
28 map(MetricExpr::parse, Expr::Metric),
29 map(LogExpr::parse, Expr::Log),
30 )),
31 opt(map(
32 tuple((
33 opt(Separator::parse),
34 BinOp::parse,
35 opt(Separator::parse),
36 BinOpOptions::parse,
37 opt(Separator::parse),
38 Expr::parse,
39 )),
40 |(_, op, _, options, _, expr)| (op, options, expr),
41 )),
42 )),
43 |(lhs, right)| match right {
44 Some((op, options, rhs)) => Self::BinOp {
45 op,
46 options,
47 lhs: Box::new(lhs),
48 rhs: Box::new(rhs),
49 },
50 None => lhs,
51 },
52 )(input)
53 }
54}
55
56#[derive(Eq, PartialEq, Debug, Clone)]
57pub struct BinOpOptions {
58 pub return_bool: bool,
59 pub card: CardType,
60 pub matching_labels: Option<Labels>,
61 pub on: bool,
62 pub include: Option<Labels>,
63}
64
65impl Parser for BinOpOptions {
66 fn parse(input: &str) -> IResult<&str, Self> {
67 println!("BinOpOptions {}", input);
68 alt((
69 map(
70 tuple((
71 OnOrIgnoringModifier::parse,
72 opt(Separator::parse),
73 opt(alt((
74 map(tag("group_left"), |_| CardType::ManyToOne),
75 map(tag("group_right"), |_| CardType::OneToMany),
76 ))),
77 opt(Separator::parse),
78 opt(map(
79 tuple((
80 cchar('('),
81 opt(Separator::parse),
82 opt(Labels::parse),
83 opt(Separator::parse),
84 cchar(')'),
85 )),
86 |(_, _, labels, _, _)| labels,
87 )),
88 )),
89 |(on_or_ignoring, _, card_type, _, include)| {
90 let card_type = match card_type {
91 Some(card_type) => card_type,
92 None => on_or_ignoring.return_bool.0,
93 };
94 let include = match include {
95 Some(include) => include,
96 None => None,
97 };
98 Self {
99 return_bool: on_or_ignoring.return_bool.1,
100 card: card_type,
101 matching_labels: on_or_ignoring.labels,
102 on: on_or_ignoring.on,
103 include,
104 }
105 },
106 ),
107 map(BoolModifier::parse, |i| Self {
108 return_bool: i.1,
109 card: i.0,
110 matching_labels: None,
111 on: false,
112 include: None,
113 }),
114 ))(input)
115 }
116}
117
118#[cfg(test)]
119mod tests {
120 use crate::{
121 basic::Literal, Duration, DurationUnit, Grouping, Identifier, JsonExpressionParser,
122 LogRangeExpr, MatchOp, Matcher, Matchers, PipelineExpr, PipelineStage,
123 RangeAggregationExpr, RangeOp, Selector, UnwrapExpr, Value, VectorAggregationExpr,
124 VectorOp,
125 };
126
127 use super::*;
128
129 #[test]
130 fn test_expr() {
131 assert_eq!(
132 Expr::parse("1.234").unwrap().1,
133 Expr::Metric(MetricExpr::Literal(Literal::from(1.234)))
134 );
135
136 assert_eq!(
137 Expr::parse("(((1.234)))").unwrap().1,
138 Expr::Metric(MetricExpr::Literal(Literal::from(1.234)))
139 );
140
141 assert_eq!(
142 Expr::parse(r#"topk(10, sum(rate({region="us-east1"} [5m]))) by (name)"#)
143 .unwrap()
144 .1,
145 Expr::Metric(MetricExpr::VectorAggregation(VectorAggregationExpr {
146 left: Box::new(MetricExpr::VectorAggregation(VectorAggregationExpr {
147 left: Box::new(MetricExpr::RangeAggregation(RangeAggregationExpr {
148 op: RangeOp::Rate,
149 log_range_expr: LogRangeExpr {
150 log_expr: LogExpr(
151 Selector::from(Some(Matchers::from(vec![Matcher {
152 op: MatchOp::EQ,
153 key: Identifier::from("region".to_string()),
154 val: Value::from("us-east1".to_string())
155 }]))),
156 None
157 ),
158 offset_expr: None,
159 unwrap_expr: None,
160 range: Duration(Literal::from(5.0), DurationUnit::M)
161 },
162 grouping: None,
163 })),
164 op: VectorOp::SUM,
165 grouping: None,
166 param: None,
167 })),
168 op: VectorOp::TOPK,
169 grouping: Some(Grouping {
170 without: false,
171 groups: Some(Labels::from(vec![Identifier::from("name".to_string())]))
172 }),
173 param: Some(Literal::from(10.0))
174 }))
175 );
176
177 assert_eq!(
178 Expr::parse(r#"count_over_time({job="mysql"} [5m])"#)
179 .unwrap()
180 .1,
181 Expr::Metric(MetricExpr::RangeAggregation(RangeAggregationExpr {
182 op: RangeOp::Count,
183 log_range_expr: LogRangeExpr {
184 log_expr: LogExpr(
185 Selector::from(Some(Matchers::from(vec![Matcher {
186 op: MatchOp::EQ,
187 key: Identifier::from("job".to_string()),
188 val: Value::from("mysql".to_string()),
189 }]))),
190 None
191 ),
192 offset_expr: None,
193 unwrap_expr: None,
194 range: Duration(Literal::from(5.0), DurationUnit::M),
195 },
196 grouping: None,
197 }))
198 );
199 }
200
201 #[test]
202 fn test_quantile_expr() {
203 assert_eq!(
204 Expr::parse(
205 r#"quantile_over_time(
206 0.99,
207 {container="ingress-nginx",service="hosted-grafana"}
208 | json
209 | unwrap response_latency_seconds [1m]
210 ) by (cluster)"#
211 )
212 .unwrap()
213 .1,
214 Expr::Metric(MetricExpr::RangeAggregation(RangeAggregationExpr {
215 op: RangeOp::Quantile(Literal(0.99)),
216 log_range_expr: LogRangeExpr {
217 log_expr: LogExpr(
218 Selector(Some(Matchers(vec![
219 Matcher {
220 op: MatchOp::EQ,
221 key: Identifier::from("container".to_owned()),
222 val: Value::from("ingress-nginx".to_owned()),
223 },
224 Matcher {
225 op: MatchOp::EQ,
226 key: Identifier::from("service".to_owned()),
227 val: Value::from("hosted-grafana".to_owned()),
228 },
229 ]))),
230 Some(PipelineExpr(vec![PipelineStage::JsonExpressionParser(
231 JsonExpressionParser(None),
232 )])),
233 ),
234 offset_expr: None,
235 unwrap_expr: Some(UnwrapExpr {
236 id: Identifier::from("response_latency_seconds".to_owned()),
237 operation: None,
238 post_filters: None,
239 }),
240 range: Duration(Literal(1.0), DurationUnit::M),
241 },
242 grouping: Some(Grouping {
243 without: false,
244 groups: Some(Labels(vec![Identifier::from("cluster".to_owned())])),
245 }),
246 }))
247 );
248 }
249
250 #[test]
251 fn test_bin_op() {
252 assert_eq!(
253 Expr::parse(r#"sum(count_over_time({app="foo"} [1m])) by (app, machine) > bool on(app) group_left(pool) sum(count_over_time({app="foo"} [1m])) by (app, pool)"#).unwrap().1,
254 Expr::BinOp {
255 op: BinOp::GT,
256 options: BinOpOptions {
257 return_bool: true,
258 card: CardType::ManyToOne,
259 matching_labels: Some(Labels::from(vec![Identifier::from("app".to_string())])),
260 on: true,
261 include: Some(Labels::from(vec![Identifier::from("pool".to_string())])),
262 },
263 lhs: Box::new(Expr::Metric(MetricExpr::VectorAggregation(
264 VectorAggregationExpr {
265 left: Box::new(MetricExpr::RangeAggregation(RangeAggregationExpr {
266 op: RangeOp::Count,
267 log_range_expr: LogRangeExpr {
268 log_expr: LogExpr(Selector::from(Some(
269 Matchers::from(vec![Matcher {
270 op: MatchOp::EQ,
271 key: Identifier::from("app".to_string()),
272 val: Value::from("foo".to_string()),
273 }])
274 )), None),
275 offset_expr: None,
276 unwrap_expr: None,
277 range: Duration(Literal::from(1.0), DurationUnit::M)
278 },
279 grouping: None
280 })),
281 op: VectorOp::SUM,
282 grouping: Some(Grouping {
283 without: false,
284 groups: Some(Labels::from(vec![
285 Identifier::from("app".to_string()),
286 Identifier::from("machine".to_string())
287 ]))
288 }),
289 param: None,
290 }
291 ))),
292 rhs: Box::new(Expr::Metric(MetricExpr::VectorAggregation(
293 VectorAggregationExpr {
294 left: Box::new(MetricExpr::RangeAggregation(RangeAggregationExpr {
295 op: RangeOp::Count,
296 log_range_expr: LogRangeExpr {
297 log_expr: LogExpr(Selector::from(Some(
298 Matchers::from(vec![Matcher {
299 op: MatchOp::EQ,
300 key: Identifier::from("app".to_string()),
301 val: Value::from("foo".to_string()),
302 }])
303 )), None),
304 offset_expr: None,
305 unwrap_expr: None,
306 range: Duration(Literal::from(1.0), DurationUnit::M)
307 },
308 grouping: None
309 })),
310 op: VectorOp::SUM,
311 grouping: Some(Grouping {
312 without: false,
313 groups: Some(Labels::from(vec![
314 Identifier::from("app".to_string()),
315 Identifier::from("pool".to_string())
316 ]))
317 }),
318 param: None,
319 }
320 )))
321 }
322 );
323 }
324}