cloudflare/pint

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.52.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

internal/parser/models.go

484lines · modecode

1package parser
2
3import (
4 "fmt"
5 "strings"
6
7 "golang.org/x/exp/slices"
8 "gopkg.in/yaml.v3"
9
10 promparser "github.com/prometheus/prometheus/promql/parser"
11
12 "github.com/cloudflare/pint/internal/comments"
13)
14
15func appendLine(lines []int, newLines ...int) []int {
16 for _, nl := range newLines {
17 var present bool
18 for _, l := range lines {
19 if l == nl {
20 present = true
21 break
22 }
23 }
24 if !present {
25 lines = append(lines, nl)
26 }
27 }
28
29 return lines
30}
31
32func nodeLines(node *yaml.Node, offset int) (lines []int) {
33 lineCount := len(strings.Split(strings.TrimSuffix(node.Value, "\n"), "\n"))
34
35 var firstLine int
36 // nolint: exhaustive
37 switch node.Style {
38 case yaml.LiteralStyle, yaml.FoldedStyle:
39 firstLine = node.Line + 1 + offset
40 default:
41 firstLine = node.Line + offset
42 }
43
44 for i := 0; i < lineCount; i++ {
45 lines = appendLine(lines, firstLine+i)
46 }
47
48 return lines
49}
50
51func NewFilePosition(l []int) FilePosition {
52 return FilePosition{Lines: l}
53}
54
55type FilePosition struct {
56 Lines []int
57}
58
59func (fp FilePosition) FirstLine() (line int) {
60 for _, l := range fp.Lines {
61 if line == 0 || l < line {
62 line = l
63 }
64 }
65 return line
66}
67
68func (fp FilePosition) LastLine() (line int) {
69 for _, l := range fp.Lines {
70 if l > line {
71 line = l
72 }
73 }
74 return line
75}
76
77func mergeComments(node *yaml.Node) (comments []string) {
78 if node.HeadComment != "" {
79 comments = append(comments, node.HeadComment)
80 }
81 if node.LineComment != "" {
82 comments = append(comments, node.LineComment)
83 }
84 if node.FootComment != "" {
85 comments = append(comments, node.FootComment)
86 }
87 for _, child := range node.Content {
88 comments = append(comments, mergeComments(child)...)
89 }
90 return comments
91}
92
93type YamlNode struct {
94 Position FilePosition
95 Value string
96}
97
98func newYamlNode(node *yaml.Node, offset int) *YamlNode {
99 n := YamlNode{
100 Position: NewFilePosition(nodeLines(node, offset)),
101 Value: node.Value,
102 }
103 if node.Alias != nil {
104 n.Value = node.Alias.Value
105 }
106 return &n
107}
108
109func newYamlKeyValue(key, val *yaml.Node, offset int) *YamlKeyValue {
110 return &YamlKeyValue{
111 Key: newYamlNode(key, offset),
112 Value: newYamlNode(val, offset),
113 }
114}
115
116type YamlKeyValue struct {
117 Key *YamlNode
118 Value *YamlNode
119}
120
121func (ykv YamlKeyValue) Lines() (lines []int) {
122 lines = appendLine(lines, ykv.Key.Position.Lines...)
123 lines = appendLine(lines, ykv.Value.Position.Lines...)
124 return lines
125}
126
127func (ykv *YamlKeyValue) IsIdentical(b *YamlKeyValue) bool {
128 if (ykv == nil) != (b == nil) {
129 return false
130 }
131 if ykv == nil {
132 return true
133 }
134 if ykv.Value.Value != b.Value.Value {
135 return false
136 }
137 return true
138}
139
140type YamlMap struct {
141 Key *YamlNode
142 Items []*YamlKeyValue
143}
144
145func (ym YamlMap) Lines() (lines []int) {
146 lines = appendLine(lines, ym.Key.Position.Lines...)
147 for _, item := range ym.Items {
148 lines = appendLine(lines, item.Lines()...)
149 }
150 return lines
151}
152
153func (ym *YamlMap) IsIdentical(b *YamlMap) bool {
154 var al, bl []string
155
156 if ym != nil && ym.Items != nil {
157 al = make([]string, 0, len(ym.Items))
158 for _, kv := range ym.Items {
159 al = append(al, fmt.Sprintf("%s: %s", kv.Key.Value, kv.Value.Value))
160 }
161 slices.Sort(al)
162 }
163
164 if b != nil && b.Items != nil {
165 bl = make([]string, 0, len(b.Items))
166 for _, kv := range b.Items {
167 bl = append(bl, fmt.Sprintf("%s: %s", kv.Key.Value, kv.Value.Value))
168 }
169 slices.Sort(bl)
170 }
171
172 return slices.Equal(al, bl)
173}
174
175func (ym YamlMap) GetValue(key string) *YamlNode {
176 for _, child := range ym.Items {
177 if child.Key.Value == key {
178 return child.Value
179 }
180 }
181 return nil
182}
183
184func newYamlMap(key, value *yaml.Node, offset int) *YamlMap {
185 ym := YamlMap{
186 Key: newYamlNode(key, offset),
187 }
188
189 var ckey *yaml.Node
190 for _, child := range value.Content {
191 if ckey != nil {
192 kv := YamlKeyValue{
193 Key: newYamlNode(ckey, offset),
194 Value: newYamlNode(child, offset),
195 }
196 ym.Items = append(ym.Items, &kv)
197 ckey = nil
198 } else {
199 ckey = child
200 }
201 }
202
203 return &ym
204}
205
206type PromQLNode struct {
207 Expr string
208 Node promparser.Expr
209 Children []*PromQLNode
210}
211
212type PromQLError struct {
213 node *PromQLNode
214 Err error
215}
216
217func (pqle PromQLError) Error() string {
218 return pqle.Err.Error()
219}
220
221func (pqle *PromQLError) Unwrap() error {
222 return pqle.Err
223}
224
225func (pqle PromQLError) Node() *PromQLNode {
226 return pqle.node
227}
228
229type PromQLExpr struct {
230 Key *YamlNode
231 Value *YamlNode
232 SyntaxError error
233 Query *PromQLNode
234}
235
236func (pqle PromQLExpr) Lines() (lines []int) {
237 lines = appendLine(lines, pqle.Key.Position.Lines...)
238 lines = appendLine(lines, pqle.Value.Position.Lines...)
239 return lines
240}
241
242func (pqle PromQLExpr) IsIdentical(b PromQLExpr) bool {
243 return pqle.Value.Value == b.Value.Value
244}
245
246func newPromQLExpr(key, val *yaml.Node, offset int) *PromQLExpr {
247 expr := PromQLExpr{
248 Key: newYamlNode(key, offset),
249 Value: newYamlNode(val, offset),
250 }
251
252 qlNode, err := DecodeExpr(expr.Value.Value)
253 if err != nil {
254 expr.SyntaxError = err
255 return &expr
256
257 }
258 expr.Query = qlNode
259 return &expr
260}
261
262type AlertingRule struct {
263 Alert YamlKeyValue
264 Expr PromQLExpr
265 For *YamlKeyValue
266 KeepFiringFor *YamlKeyValue
267 Labels *YamlMap
268 Annotations *YamlMap
269}
270
271func (ar AlertingRule) Lines() (lines []int) {
272 lines = appendLine(lines, ar.Alert.Lines()...)
273 lines = appendLine(lines, ar.Expr.Lines()...)
274 if ar.For != nil {
275 lines = appendLine(lines, ar.For.Lines()...)
276 }
277 if ar.KeepFiringFor != nil {
278 lines = appendLine(lines, ar.KeepFiringFor.Lines()...)
279 }
280 if ar.Labels != nil {
281 lines = appendLine(lines, ar.Labels.Lines()...)
282 }
283 if ar.Annotations != nil {
284 lines = appendLine(lines, ar.Annotations.Lines()...)
285 }
286 slices.Sort(lines)
287 return lines
288}
289
290func (ar *AlertingRule) IsIdentical(b *AlertingRule) bool {
291 if (ar == nil) != (b == nil) {
292 return false
293 }
294 if ar == nil {
295 return true
296 }
297 if !ar.Alert.IsIdentical(&b.Alert) {
298 return false
299 }
300 if !ar.Expr.IsIdentical(b.Expr) {
301 return false
302 }
303 if !ar.For.IsIdentical(b.For) {
304 return false
305 }
306 if !ar.KeepFiringFor.IsIdentical(b.KeepFiringFor) {
307 return false
308 }
309 if !ar.Labels.IsIdentical(b.Labels) {
310 return false
311 }
312 if !ar.Annotations.IsIdentical(b.Annotations) {
313 return false
314 }
315 return true
316}
317
318type RecordingRule struct {
319 Record YamlKeyValue
320 Expr PromQLExpr
321 Labels *YamlMap
322}
323
324func (rr RecordingRule) Lines() (lines []int) {
325 lines = appendLine(lines, rr.Record.Lines()...)
326 lines = appendLine(lines, rr.Expr.Lines()...)
327 if rr.Labels != nil {
328 lines = appendLine(lines, rr.Labels.Lines()...)
329 }
330 slices.Sort(lines)
331 return lines
332}
333
334func (rr *RecordingRule) IsIdentical(b *RecordingRule) bool {
335 if (rr == nil) != (b == nil) {
336 return false
337 }
338 if rr == nil {
339 return true
340 }
341 if !rr.Record.IsIdentical(&b.Record) {
342 return false
343 }
344 if !rr.Expr.IsIdentical(b.Expr) {
345 return false
346 }
347 if !rr.Labels.IsIdentical(b.Labels) {
348 return false
349 }
350 return true
351}
352
353type ParseError struct {
354 Fragment string
355 Err error
356 Line int
357}
358
359type Rule struct {
360 AlertingRule *AlertingRule
361 RecordingRule *RecordingRule
362 Comments []comments.Comment
363 Error ParseError
364}
365
366func (r Rule) IsIdentical(b Rule) bool {
367 if r.Type() != b.Type() {
368 return false
369 }
370 if !r.AlertingRule.IsIdentical(b.AlertingRule) {
371 return false
372 }
373 if !r.RecordingRule.IsIdentical(b.RecordingRule) {
374 return false
375 }
376
377 ac := make([]string, 0, len(r.Comments))
378 for _, c := range r.Comments {
379 ac = append(ac, c.Value.String())
380 }
381 slices.Sort(ac)
382
383 bc := make([]string, 0, len(r.Comments))
384 for _, c := range b.Comments {
385 bc = append(bc, c.Value.String())
386 }
387 slices.Sort(bc)
388
389 return slices.Equal(ac, bc)
390}
391
392func (r Rule) IsSame(nr Rule) bool {
393 if (r.AlertingRule != nil) != (nr.AlertingRule != nil) {
394 return false
395 }
396 if (r.RecordingRule != nil) != (nr.RecordingRule != nil) {
397 return false
398 }
399 if r.Error != nr.Error {
400 return false
401 }
402 if !slices.Equal(r.Lines(), nr.Lines()) {
403 return false
404 }
405 return true
406}
407
408func (r Rule) Name() string {
409 if r.RecordingRule != nil {
410 return r.RecordingRule.Record.Value.Value
411 }
412 if r.AlertingRule != nil {
413 return r.AlertingRule.Alert.Value.Value
414 }
415 return ""
416}
417
418func (r Rule) Expr() PromQLExpr {
419 if r.RecordingRule != nil {
420 return r.RecordingRule.Expr
421 }
422 return r.AlertingRule.Expr
423}
424
425func (r Rule) Lines() []int {
426 if r.RecordingRule != nil {
427 return r.RecordingRule.Lines()
428 }
429 if r.AlertingRule != nil {
430 return r.AlertingRule.Lines()
431 }
432 if r.Error.Err != nil {
433 return []int{r.Error.Line}
434 }
435 return nil
436}
437
438func (r Rule) LineRange() []int {
439 var lmin, lmax int
440 for i, line := range r.Lines() {
441 if i == 0 {
442 lmin = line
443 lmax = line
444 continue
445 }
446 if line < lmin {
447 lmin = line
448 }
449 if line > lmax {
450 lmax = line
451 }
452 }
453
454 lines := []int{}
455 for i := lmin; i <= lmax; i++ {
456 lines = append(lines, i)
457 }
458 return lines
459}
460
461type RuleType string
462
463const (
464 AlertingRuleType RuleType = "alerting"
465 RecordingRuleType RuleType = "recording"
466 InvalidRuleType RuleType = "invalid"
467)
468
469func (r Rule) Type() RuleType {
470 if r.AlertingRule != nil {
471 return AlertingRuleType
472 }
473 if r.RecordingRule != nil {
474 return RecordingRuleType
475 }
476 return InvalidRuleType
477}
478
479type Result struct {
480 Path string
481 Error error
482 Content []byte
483 Rules []Rule
484}
485