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 write!(f, "align to {time} using {function}")
212 }
213 Aggregate::GroupBy(GroupBy {
214 span: _,
215 function,
216 tags: fields,
217 }) => {
218 if let Some((field, rest)) = fields.split_first() {
219 write!(f, "group by ")?;
220 escape_ident(f, field)?;
221 for field in rest {
222 write!(f, ", ")?;
223 escape_ident(f, field)?;
224 }
225 } else {
226 write!(f, "group ")?;
227 }
228 write!(f, " using {function}")
229 }
230 Aggregate::Bucket(BucketBy {
231 span: _,
232 function,
233 time,
234 tags: fields,
235 spec,
236 }) => {
237 if let Some((field, rest)) = fields.split_first() {
238 write!(f, "bucket by ")?;
239 escape_ident(f, field)?;
240 for field in rest {
241 write!(f, ", ")?;
242 escape_ident(f, field)?;
243 }
244 } else {
245 write!(f, "bucket ")?;
246 }
247 write!(f, " to {time} using {function}")?;
248 let mode_prefix = if let BucketType::InterpolateCumulativeHistogram(mode) = function
250 {
251 Some(mode)
252 } else {
253 None
254 };
255 if let Some((first, rest)) = spec.split_first() {
256 write!(f, "(")?;
257 if let Some(mode) = mode_prefix {
258 write!(f, "{mode}, ")?;
259 }
260 write!(f, "{first}")?;
261 for s in rest {
262 write!(f, ", {s}")?;
263 }
264 write!(f, ")")?;
265 }
266 Ok(())
267 }
268 }
269 }
270}
271impl Display for Mapping {
272 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
273 match self {
274 Mapping {
275 function:
276 MapFunction::Builtin(
277 MapType::Mul
278 | MapType::Div
279 | MapType::Add
280 | MapType::Sub
281 | MapType::InterpolateLinear,
282 ),
283 arg,
284 } => {
285 write!(f, " {}", self.function)?;
286 if let Some(arg) = arg {
287 write!(f, " {arg}")?;
288 }
289 }
290 Mapping {
291 function:
292 MapFunction::Builtin(
293 MapType::Abs
294 | MapType::Max
295 | MapType::Min
296 | MapType::Rate
297 | MapType::FillConst
298 | MapType::FillPrev
299 | MapType::Increase
300 | MapType::FilterLt
301 | MapType::FilterGt
302 | MapType::FilterEq
303 | MapType::FilterNe
304 | MapType::FilterGe
305 | MapType::FilterLe
306 | MapType::IsLt
307 | MapType::IsGt
308 | MapType::IsEq
309 | MapType::IsNe
310 | MapType::IsGe
311 | MapType::IsLe,
312 ),
313 arg,
314 } => {
315 write!(f, "{}", self.function)?;
316 if let Some(arg) = arg {
317 write!(f, "({arg})")?;
318 }
319 }
320 Mapping {
321 function: MapFunction::UserDefined(func),
322 arg,
323 } => {
324 write!(f, " {func}")?;
325 if let Some(arg) = arg {
326 write!(f, " {arg}")?;
327 }
328 }
329 }
330
331 Ok(())
332 }
333}