cloudflare/pint

Public

mirrored from https://github.com/cloudflare/pintAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.77.1

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

internal/parser/decode.go

124lines · modecode

1package parser
2
3import (
4 "encoding/json"
5 "errors"
6 "slices"
7
8 promParser "github.com/prometheus/prometheus/promql/parser"
9)
10
11type PromQLExpr struct {
12 Value *YamlNode
13 SyntaxError error
14 Query *PromQLNode
15}
16
17// PromQLNode is used to turn the parsed PromQL query expression into a tree.
18// This allows us to walk the tree up & down and look for either parents
19// or children of specific type. Which is useful if you, for example,
20// want to check if all vector selectors are wrapped inside function
21// calls etc.
22type PromQLNode struct {
23 Parent *PromQLNode
24 Expr promParser.Node
25 Children []*PromQLNode
26}
27
28func (pn PromQLNode) MarshalJSON() ([]byte, error) {
29 return json.Marshal(pn.Expr.String())
30}
31
32// Tree takes a parsed PromQL node and turns it into a Node
33// instance with parent and children populated.
34func tree(expr promParser.Node, parent *PromQLNode) *PromQLNode {
35 n := PromQLNode{
36 Parent: parent,
37 Expr: expr,
38 Children: nil,
39 }
40 for _, child := range promParser.Children(expr) {
41 n.Children = append(n.Children, tree(child, &n))
42 }
43
44 return &n
45}
46
47// WalkUpExpr allows to iterate a promQLNode node looking for
48// parents of specific type.
49// Prometheus parser returns interfaces which makes it more difficult
50// to figure out what kind of node we're dealing with, hence this
51// helper takes a type parameter it tries to cast.
52// It starts by checking the node passed to it and then walks
53// up by visiting all parent nodes.
54func WalkUpExpr[T promParser.Node](node *PromQLNode) (nodes []*PromQLNode) {
55 if node == nil {
56 return nodes
57 }
58 if _, ok := node.Expr.(T); ok {
59 nodes = append(nodes, node)
60 }
61 if node.Parent != nil {
62 nodes = append(nodes, WalkUpExpr[T](node.Parent)...)
63 }
64 return nodes
65}
66
67// WalkDownExpr works just like WalkUpExpr but it walks the tree
68// down, visiting all children.
69// It also starts by checking the node passed to it before walking
70// down the tree.
71func WalkDownExpr[T promParser.Node](node *PromQLNode) (nodes []*PromQLNode) {
72 if _, ok := node.Expr.(T); ok {
73 nodes = append(nodes, node)
74 }
75 for _, child := range node.Children {
76 nodes = append(nodes, WalkDownExpr[T](child)...)
77 }
78 return nodes
79}
80
81// WalkUpParent works like WalkUpExpr but checks the parent
82// (if present) instead of the node itself.
83// It returns the nodes where the parent is of given type.
84func WalkUpParent[T promParser.Node](node *PromQLNode) (nodes []*PromQLNode) {
85 if node == nil || node.Parent == nil {
86 return nodes
87 }
88 if _, ok := node.Parent.Expr.(T); ok {
89 nodes = append(nodes, node)
90 }
91 if node.Parent != nil {
92 nodes = append(nodes, WalkUpParent[T](node.Parent)...)
93 }
94 return nodes
95}
96
97func DecodeExpr(expr string) (*PromQLNode, error) {
98 node, err := promParser.ParseExpr(expr)
99 if err != nil {
100 var errorList promParser.ParseErrors
101 if errors.As(err, &errorList) {
102 // Find the error pointing at the shortest query fragment.
103 slices.SortFunc(errorList, func(a, b promParser.ParseErr) int {
104 ar := a.PositionRange.End - a.PositionRange.Start
105 br := b.PositionRange.End - b.PositionRange.Start
106 switch {
107 case ar < br:
108 return -1
109 case ar > br:
110 return 1
111 default:
112 return 0
113 }
114 })
115 for _, el := range errorList {
116 if el.PositionRange.Start > 0 && el.PositionRange.End > 0 {
117 return nil, promParser.ParseErrors{el}
118 }
119 }
120 }
121 return nil, err
122 }
123 return tree(node, nil), nil
124}
125