-
Notifications
You must be signed in to change notification settings - Fork 6
/
metricquery.go
100 lines (79 loc) · 2.63 KB
/
metricquery.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
package ddqp
import (
"fmt"
"strings"
"github.com/alecthomas/participle/v2"
"github.com/alecthomas/participle/v2/lexer"
)
type MetricQuery struct {
Pos lexer.Position
Query []*Query `parser:"@@"`
}
func (mq *MetricQuery) String() string {
return mq.Query[0].String()
}
type Query struct {
Pos lexer.Position
Aggregator string `parser:"@Ident"`
SpaceAggregationCondition string `parser:"( '(' @SpaceAggregatorCondition ')' )?"`
Separator string `parser:"':'"`
MetricName string `parser:"@Ident( @'.' @Ident)*"`
Filters *MetricFilter `parser:"'{' @@ '}'"`
By string `parser:"Ident?"`
Grouping []string `parser:"'{'? ( @Ident ( ',' @Ident )* )? '}'?"`
Function []*Function `parser:"( @@ ( '.' @@ )* )?"`
}
func (q *Query) String() string {
base := q.Aggregator
if q.SpaceAggregationCondition != "" {
base = fmt.Sprintf("%s(%s)", base, q.SpaceAggregationCondition)
}
base = fmt.Sprintf("%s:%s{%s}", base, q.MetricName, q.Filters.String())
if len(q.Grouping) > 0 {
base = fmt.Sprintf("%s by {%s}", base, strings.Join(q.Grouping, ","))
}
if len(q.Function) > 0 {
funcs := []string{}
for _, v := range q.Function {
funcs = append(funcs, v.String())
}
return fmt.Sprintf("%s.%s", base, strings.Join(funcs, "."))
}
return base
}
type Function struct {
Name string `"." @Ident`
Args []*Value `"(" ( @@ ( "," @@ )* )? ")"`
}
func (f *Function) String() string {
args := []string{}
for _, v := range f.Args {
args = append(args, v.String())
}
return fmt.Sprintf("%s(%s)", f.Name, strings.Join(args, ","))
}
type Bool bool
func (b *Bool) Capture(v []string) error { *b = v[0] == "true"; return nil }
func (b *Bool) String() string { return fmt.Sprintf("%v", *b) }
// NewMetricQueryParser returns a Parser which is capable of interpretting
// a metric query.
func NewMetricQueryParser() *MetricQueryParser {
mqp := &MetricQueryParser{
parser: participle.MustBuild[MetricQuery](
participle.Lexer(lex),
participle.Unquote("String"),
),
}
return mqp
}
// MetricQueryParser is parser returned when calling NewMetricQueryParser.
type MetricQueryParser struct {
parser *participle.Parser[MetricQuery]
}
// Parse sanitizes the query string and returns the AST and any error.
func (mqp *MetricQueryParser) Parse(query string) (*MetricQuery, error) {
// the parser doesn't handle queries that are split up across multiple lines
sanitized := strings.ReplaceAll(query, "\n", "")
// return the raw parsed outpu
return mqp.parser.ParseString("", sanitized)
}