1use std::fmt::Display;
2
3use crate::{
4 Query,
5 linker::MapFunction,
6 query::{
7 Aggregate, Align, As, BucketBy, Cmp, Filter, GroupBy, Mapping, MetricId, RelativeTime,
8 Source, Time, TimeRange, TimeUnit,
9 },
10 types::{BucketType, MapType, Parameterized},
11};
12
13fn escape_ident(f: &mut std::fmt::Formatter<'_>, ident: &str) -> std::fmt::Result {
14 write!(f, "`{}`", ident.replace('\\', "\\\\").replace('`', "\\`"))
15}
16
17impl Display for Query {
18 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
19 for param in self.params() {
20 writeln!(f, "param ${}: {};", param.name, param.typ)?;
21 }
22
23 match self {
24 Query::Simple {
25 sample,
26 source,
27 filters,
28 aggregates,
29 directives: _,
30 params: _,
31 } => {
32 writeln!(f, "{source}")?;
33 if let Some(sample) = sample {
34 writeln!(f, "| sample {sample}")?;
35 }
36 for filter in filters {
37 match filter {
38 crate::query::FilterOrIfDef::Filter(filter) => {
39 writeln!(f, "| where {filter}")?;
40 }
41 crate::query::FilterOrIfDef::Ifdef { param, filter } => {
42 writeln!(f, "| ifdef(${}) {{ where {filter} }}", param.name)?;
43 }
44 }
45 }
46 for aggregate in aggregates {
47 writeln!(f, " {aggregate}")?;
48 }
49 }
50 Query::Compute {
51 left,
52 right,
53 name,
54 op,
55 aggregates,
56 directives: _,
57 params: _,
58 } => {
59 writeln!(f, "( {left}, {right} )")?;
60 writeln!(f, "| compute {name} using {op}")?;
61 for aggregate in aggregates {
62 writeln!(f, " {aggregate}")?;
63 }
64 }
65 }
66
67 Ok(())
68 }
69}
70
71impl Display for Source {
72 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
73 let Source {
74 metric_id: MetricId { dataset, metric },
75 time,
76 } = self;
77 match dataset {
78 Parameterized::Concrete(dataset) => escape_ident(f, dataset)?,
79 Parameterized::Param { span: _, param } => {
80 write!(f, "$")?;
81 escape_ident(f, param.name.as_str())?;
82 }
83 }
84 write!(f, ":")?;
85 escape_ident(f, metric)?;
86 if let Some(time) = time {
87 write!(f, "{time}")?;
88 }
89 Ok(())
90 }
91}
92
93impl Display for TimeRange {
94 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
95 write!(f, "[{}..", self.start)?;
96 if let Some(end) = &self.end {
97 write!(f, "{end}]")?;
98 } else {
99 write!(f, "]")?;
100 }
101 Ok(())
102 }
103}
104
105impl Display for Time {
106 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
107 match self {
108 Time::Relative(relative_time) => write!(f, "{relative_time}"),
109 Time::Timestamp(t) => write!(f, "{t}"),
110 Time::RFC3339(date_time) => write!(f, "{date_time}"),
111 Time::Modifier(m) => write!(f, "{m}"),
112 }
113 }
114}
115
116impl Display for RelativeTime {
117 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
118 write!(f, "{}{}", self.value, self.unit)
119 }
120}
121
122impl Display for TimeUnit {
123 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
124 match self {
125 TimeUnit::Millisecond => write!(f, "ms"),
126 TimeUnit::Second => write!(f, "s"),
127 TimeUnit::Minute => write!(f, "m"),
128 TimeUnit::Hour => write!(f, "h"),
129 TimeUnit::Day => write!(f, "d"),
130 TimeUnit::Week => write!(f, "w"),
131 TimeUnit::Month => write!(f, "M"),
132 TimeUnit::Year => write!(f, "y"),
133 }
134 }
135}
136
137impl Display for As {
138 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
139 let As { name } = self;
140 write!(f, "as {name}")
141 }
142}
143
144impl Display for Filter {
145 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
146 match self {
147 Filter::And(filters) => {
148 if let Some((first, rest)) = filters.split_first() {
149 write!(f, "({first}")?;
150 for filter in rest {
151 write!(f, " and {filter}")?;
152 }
153 write!(f, ")")?;
154 }
155 Ok(())
156 }
157 Filter::Or(filters) => {
158 if let Some((first, rest)) = filters.split_first() {
159 write!(f, "({first}")?;
160 for filter in rest {
161 write!(f, " or {filter}")?;
162 }
163 write!(f, ")")?;
164 }
165 Ok(())
166 }
167 Filter::Not(filter) => {
168 write!(f, "not {filter}")
169 }
170 Filter::Cmp { field, rhs } => {
171 escape_ident(f, field)?;
172 write!(f, " {rhs}")
173 }
174 }
175 }
176}
177
178impl Display for Cmp {
179 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
180 match self {
181 Cmp::Eq(v) => write!(f, "== {v}"),
182 Cmp::Ne(v) => write!(f, "!= {v}"),
183 Cmp::Gt(v) => write!(f, "> {v}"),
184 Cmp::Ge(v) => write!(f, ">= {v}"),
185 Cmp::Lt(v) => write!(f, "< {v}"),
186 Cmp::Le(v) => write!(f, "<= {v}"),
187 Cmp::Is(v) => write!(f, "is {v}"),
188 Cmp::RegEx(r) => match r {
189 Parameterized::Concrete(r) => write!(f, "== {}", r.as_ref()),
190 Parameterized::Param { span: _, param } => write!(f, "== ${}", param.name),
191 },
192 Cmp::RegExNot(r) => match r {
193 Parameterized::Concrete(r) => write!(f, "!= {}", r.as_ref()),
194 Parameterized::Param { span: _, param } => write!(f, "!= ${}", param.name),
195 },
196 }
197 }
198}
199
200impl Display for Aggregate {
201 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
202 write!(f, "| ")?;
203 match self {
204 Aggregate::As(As { name }) => write!(f, "as {name}"),
205 Aggregate::Map(Mapping {
206 function: MapFunction::Builtin(MapType::Rate),
207 arg: None,
208 }) => write!(f, "map rate"),
209 Aggregate::Map(map) => write!(f, "map {map}"),
210 Aggregate::Align(Align { function, time }) => {
211 if let Some(time) = time {
212 write!(f, "align to {time} using {function}")
213 } else {
214 write!(f, "align using {function}")
215 }
216 }
217 Aggregate::GroupBy(GroupBy {
218 span: _,
219 function,
220 tags: fields,
221 }) => {
222 if let Some((field, rest)) = fields.split_first() {
223 write!(f, "group by ")?;
224 escape_ident(f, field)?;
225 for field in rest {
226 write!(f, ", ")?;
227 escape_ident(f, field)?;
228 }
229 } else {
230 write!(f, "group ")?;
231 }
232 write!(f, " using {function}")
233 }
234 Aggregate::Bucket(BucketBy {
235 span: _,
236 function,
237 time,
238 tags: fields,
239 spec,
240 }) => {
241 if let Some((field, rest)) = fields.split_first() {
242 write!(f, "bucket by ")?;
243 escape_ident(f, field)?;
244 for field in rest {
245 write!(f, ", ")?;
246 escape_ident(f, field)?;
247 }
248 } else {
249 write!(f, "bucket ")?;
250 }
251 if let Some(time) = time {
252 write!(f, " to {time} using {function}")?;
253 } else {
254 write!(f, " using {function}")?;
255 }
256 let mode_prefix = if let BucketType::InterpolateCumulativeHistogram(mode) = function
258 {
259 Some(mode)
260 } else {
261 None
262 };
263 if let Some((first, rest)) = spec.split_first() {
264 write!(f, "(")?;
265 if let Some(mode) = mode_prefix {
266 write!(f, "{mode}, ")?;
267 }
268 write!(f, "{first}")?;
269 for s in rest {
270 write!(f, ", {s}")?;
271 }
272 write!(f, ")")?;
273 }
274 Ok(())
275 }
276 }
277 }
278}
279impl Display for Mapping {
280 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
281 match self {
282 Mapping {
283 function:
284 MapFunction::Builtin(
285 MapType::Mul
286 | MapType::Div
287 | MapType::Add
288 | MapType::Sub
289 | MapType::InterpolateLinear,
290 ),
291 arg,
292 } => {
293 write!(f, " {}", self.function)?;
294 if let Some(arg) = arg {
295 write!(f, " {arg}")?;
296 }
297 }
298 Mapping {
299 function:
300 MapFunction::Builtin(
301 MapType::Abs
302 | MapType::Max
303 | MapType::Min
304 | MapType::Rate
305 | MapType::FillConst
306 | MapType::FillPrev
307 | MapType::Increase
308 | MapType::FilterLt
309 | MapType::FilterGt
310 | MapType::FilterEq
311 | MapType::FilterNe
312 | MapType::FilterGe
313 | MapType::FilterLe
314 | MapType::IsLt
315 | MapType::IsGt
316 | MapType::IsEq
317 | MapType::IsNe
318 | MapType::IsGe
319 | MapType::IsLe,
320 ),
321 arg,
322 } => {
323 write!(f, "{}", self.function)?;
324 if let Some(arg) = arg {
325 write!(f, "({arg})")?;
326 }
327 }
328 Mapping {
329 function: MapFunction::UserDefined(func),
330 arg,
331 } => {
332 write!(f, " {func}")?;
333 if let Some(arg) = arg {
334 write!(f, " {arg}")?;
335 }
336 }
337 }
338
339 Ok(())
340 }
341}