master
go 103 lines 2.23 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package selectorcore
4
5 import (
6 "fmt"
7 "slices"
8 "strings"
9 )
10
11 // Meta contains selector metadata needed by template compilers/engines.
12 type Meta struct {
13 MetricNames []string
14 ConstrainedLabelKeys []string
15 }
16
17 // Compiled is a selector with stable metadata.
18 type Compiled interface {
19 Selector
20 Meta() Meta
21 }
22
23 type compiledSelector struct {
24 Selector
25 meta Meta
26 }
27
28 func (c compiledSelector) Meta() Meta {
29 return Meta{
30 MetricNames: append([]string(nil), c.meta.MetricNames...),
31 ConstrainedLabelKeys: append([]string(nil), c.meta.ConstrainedLabelKeys...),
32 }
33 }
34
35 // ParseCompiled parses a selector expression and returns matcher + metadata.
36 func ParseCompiled(expr string) (Compiled, error) {
37 expanded := unsugarExpr(expr)
38 terms, err := splitSelectorTerms(expanded)
39 if err != nil {
40 return nil, err
41 }
42 if len(terms) == 0 {
43 return nil, fmt.Errorf("invalid selector syntax: %q", expr)
44 }
45
46 parts := make([]Selector, 0, len(terms))
47 metricNames := map[string]struct{}{}
48 keys := map[string]struct{}{}
49
50 for _, term := range terms {
51 sel, err := parseSelector(term)
52 if err != nil {
53 return nil, err
54 }
55 parts = append(parts, sel)
56
57 sub := reLV.FindStringSubmatch(strings.TrimSpace(term))
58 if sub == nil {
59 return nil, fmt.Errorf("invalid selector syntax: %q", term)
60 }
61 name, op, pattern := sub[1], sub[2], strings.Trim(sub[3], "\"")
62 if name == MetricNameLabel {
63 for _, mn := range metricNameCandidates(op, pattern) {
64 metricNames[mn] = struct{}{}
65 }
66 continue
67 }
68 keys[name] = struct{}{}
69 }
70
71 out := compiledSelector{Selector: parts[0]}
72 if len(parts) > 1 {
73 out.Selector = And(parts[0], parts[1], parts[2:]...)
74 }
75 out.meta = Meta{
76 MetricNames: mapKeysSorted(metricNames),
77 ConstrainedLabelKeys: mapKeysSorted(keys),
78 }
79 return out, nil
80 }
81
82 func metricNameCandidates(op, pattern string) []string {
83 switch op {
84 case OpEqual:
85 return []string{pattern}
86 case OpSimplePatterns:
87 if strings.ContainsAny(pattern, "*?[]") {
88 return nil
89 }
90 return []string{pattern}
91 default:
92 return nil
93 }
94 }
95
96 func mapKeysSorted(set map[string]struct{}) []string {
97 out := make([]string, 0, len(set))
98 for key := range set {
99 out = append(out, key)
100 }
101 slices.Sort(out)
102 return out
103 }