cloudflare/pint

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.62.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

internal/parser/models.go

365lines · modecode

1package parser
2
3import (
4 "fmt"
5 "strconv"
6 "strings"
7
8 "golang.org/x/exp/slices"
9 "gopkg.in/yaml.v3"
10
11 "github.com/cloudflare/pint/internal/comments"
12)
13
14func nodeLines(node *yaml.Node, offset int) (lr LineRange) {
15 switch {
16 case node.Value == "":
17 lr.First = node.Line + offset
18 case node.Style == yaml.LiteralStyle || node.Style == yaml.FoldedStyle:
19 lr.First = node.Line + 1 + offset
20 default:
21 lr.First = node.Line + offset
22 }
23 lr.Last = lr.First + len(strings.Split(strings.TrimSuffix(node.Value, "\n"), "\n")) - 1
24 return lr
25}
26
27func nodeValue(node *yaml.Node) string {
28 if node.Alias != nil {
29 return node.Alias.Value
30 }
31 return node.Value
32}
33
34func mergeComments(node *yaml.Node) (comments []string) {
35 if node.HeadComment != "" {
36 comments = append(comments, node.HeadComment)
37 }
38 if node.LineComment != "" {
39 comments = append(comments, node.LineComment)
40 }
41 if node.FootComment != "" {
42 comments = append(comments, node.FootComment)
43 }
44 for _, child := range node.Content {
45 comments = append(comments, mergeComments(child)...)
46 }
47 return comments
48}
49
50type YamlNode struct {
51 Value string
52 Lines LineRange
53}
54
55func (yn *YamlNode) IsIdentical(b *YamlNode) bool {
56 if (yn == nil) != (b == nil) {
57 return false
58 }
59 if yn == nil {
60 return true
61 }
62 if yn.Value != b.Value {
63 return false
64 }
65 return true
66}
67
68func newYamlNode(node *yaml.Node, offset int) *YamlNode {
69 n := YamlNode{
70 Lines: nodeLines(node, offset),
71 Value: nodeValue(node),
72 }
73 return &n
74}
75
76func newYamlNodeWithKey(key, node *yaml.Node, offset int) *YamlNode {
77 keyLines := nodeLines(key, offset)
78 valLines := nodeLines(node, offset)
79 n := YamlNode{
80 Lines: LineRange{
81 First: min(keyLines.First, valLines.First),
82 Last: max(keyLines.Last, valLines.Last),
83 },
84 Value: nodeValue(node),
85 }
86 return &n
87}
88
89type YamlKeyValue struct {
90 Key *YamlNode
91 Value *YamlNode
92}
93
94type YamlMap struct {
95 Key *YamlNode
96 Items []*YamlKeyValue
97 Lines LineRange
98}
99
100func (ym *YamlMap) IsIdentical(b *YamlMap) bool {
101 var al, bl []string
102
103 if ym != nil && ym.Items != nil {
104 al = make([]string, 0, len(ym.Items))
105 for _, kv := range ym.Items {
106 al = append(al, fmt.Sprintf("%s: %s", kv.Key.Value, kv.Value.Value))
107 }
108 slices.Sort(al)
109 }
110
111 if b != nil && b.Items != nil {
112 bl = make([]string, 0, len(b.Items))
113 for _, kv := range b.Items {
114 bl = append(bl, fmt.Sprintf("%s: %s", kv.Key.Value, kv.Value.Value))
115 }
116 slices.Sort(bl)
117 }
118
119 return slices.Equal(al, bl)
120}
121
122func (ym YamlMap) GetValue(key string) *YamlNode {
123 for _, child := range ym.Items {
124 if child.Key.Value == key {
125 return child.Value
126 }
127 }
128 return nil
129}
130
131func newYamlMap(key, value *yaml.Node, offset int) *YamlMap {
132 ym := YamlMap{
133 Lines: LineRange{
134 First: key.Line + offset,
135 Last: key.Line + offset,
136 },
137 Key: newYamlNode(key, offset),
138 }
139
140 var ckey *yaml.Node
141 for _, child := range value.Content {
142 if ckey != nil {
143 kv := YamlKeyValue{
144 Key: newYamlNode(ckey, offset),
145 Value: newYamlNode(child, offset),
146 }
147 if kv.Value.Lines.Last > ym.Lines.Last {
148 ym.Lines.Last = kv.Value.Lines.Last
149 }
150 ym.Items = append(ym.Items, &kv)
151 ckey = nil
152 } else {
153 ckey = child
154 }
155 }
156
157 return &ym
158}
159
160func (pqle PromQLExpr) IsIdentical(b PromQLExpr) bool {
161 return pqle.Value.Value == b.Value.Value
162}
163
164func newPromQLExpr(key, val *yaml.Node, offset int) *PromQLExpr {
165 expr := PromQLExpr{
166 Value: newYamlNodeWithKey(key, val, offset),
167 }
168
169 qlNode, err := DecodeExpr(expr.Value.Value)
170 if err != nil {
171 expr.SyntaxError = err
172 return &expr
173
174 }
175 expr.Query = qlNode
176 return &expr
177}
178
179type AlertingRule struct {
180 Expr PromQLExpr
181 For *YamlNode
182 KeepFiringFor *YamlNode
183 Labels *YamlMap
184 Annotations *YamlMap
185 Alert YamlNode
186}
187
188func (ar *AlertingRule) IsIdentical(b *AlertingRule) bool {
189 if (ar == nil) != (b == nil) {
190 return false
191 }
192 if ar == nil {
193 return true
194 }
195 if !ar.Alert.IsIdentical(&b.Alert) {
196 return false
197 }
198 if !ar.Expr.IsIdentical(b.Expr) {
199 return false
200 }
201 if !ar.For.IsIdentical(b.For) {
202 return false
203 }
204 if !ar.KeepFiringFor.IsIdentical(b.KeepFiringFor) {
205 return false
206 }
207 if !ar.Labels.IsIdentical(b.Labels) {
208 return false
209 }
210 if !ar.Annotations.IsIdentical(b.Annotations) {
211 return false
212 }
213 return true
214}
215
216type RecordingRule struct {
217 Expr PromQLExpr
218 Labels *YamlMap
219 Record YamlNode
220}
221
222func (rr *RecordingRule) IsIdentical(b *RecordingRule) bool {
223 if (rr == nil) != (b == nil) {
224 return false
225 }
226 if rr == nil {
227 return true
228 }
229 if !rr.Record.IsIdentical(&b.Record) {
230 return false
231 }
232 if !rr.Expr.IsIdentical(b.Expr) {
233 return false
234 }
235 if !rr.Labels.IsIdentical(b.Labels) {
236 return false
237 }
238 return true
239}
240
241// Use insread of StrictError.
242type ParseError struct {
243 Err error
244 Details string
245 Line int
246}
247
248func (pe ParseError) Error() string {
249 return fmt.Sprintf("error at line %d: %s", pe.Line, pe.Err)
250}
251
252type LineRange struct {
253 First int
254 Last int
255}
256
257func (lr LineRange) String() string {
258 if lr.First == lr.Last {
259 return strconv.Itoa(lr.First)
260 }
261 return fmt.Sprintf("%d-%d", lr.First, lr.Last)
262}
263
264func (lr LineRange) Expand() []int {
265 lines := make([]int, 0, lr.Last-lr.First+1)
266 for i := lr.First; i <= lr.Last; i++ {
267 lines = append(lines, i)
268 }
269 return lines
270}
271
272type Rule struct {
273 AlertingRule *AlertingRule
274 RecordingRule *RecordingRule
275 Error ParseError
276 Comments []comments.Comment
277 Lines LineRange
278}
279
280func (r Rule) IsIdentical(b Rule) bool {
281 if r.Type() != b.Type() {
282 return false
283 }
284 if !r.AlertingRule.IsIdentical(b.AlertingRule) {
285 return false
286 }
287 if !r.RecordingRule.IsIdentical(b.RecordingRule) {
288 return false
289 }
290
291 ac := make([]string, 0, len(r.Comments))
292 for _, c := range r.Comments {
293 ac = append(ac, c.Value.String())
294 }
295 slices.Sort(ac)
296
297 bc := make([]string, 0, len(r.Comments))
298 for _, c := range b.Comments {
299 bc = append(bc, c.Value.String())
300 }
301 slices.Sort(bc)
302
303 return slices.Equal(ac, bc)
304}
305
306func (r Rule) IsSame(nr Rule) bool {
307 if (r.AlertingRule != nil) != (nr.AlertingRule != nil) {
308 return false
309 }
310 if (r.RecordingRule != nil) != (nr.RecordingRule != nil) {
311 return false
312 }
313 if r.Error != nr.Error {
314 return false
315 }
316 if r.Lines.First != nr.Lines.First {
317 return false
318 }
319 if r.Lines.Last != nr.Lines.Last {
320 return false
321 }
322 return true
323}
324
325func (r Rule) Name() string {
326 if r.RecordingRule != nil {
327 return r.RecordingRule.Record.Value
328 }
329 if r.AlertingRule != nil {
330 return r.AlertingRule.Alert.Value
331 }
332 return ""
333}
334
335func (r Rule) Expr() PromQLExpr {
336 if r.RecordingRule != nil {
337 return r.RecordingRule.Expr
338 }
339 return r.AlertingRule.Expr
340}
341
342type RuleType string
343
344const (
345 AlertingRuleType RuleType = "alerting"
346 RecordingRuleType RuleType = "recording"
347 InvalidRuleType RuleType = "invalid"
348)
349
350func (r Rule) Type() RuleType {
351 if r.AlertingRule != nil {
352 return AlertingRuleType
353 }
354 if r.RecordingRule != nil {
355 return RecordingRuleType
356 }
357 return InvalidRuleType
358}
359
360type Result struct {
361 Path string
362 Error error
363 Content []byte
364 Rules []Rule
365}
366