cloudflare/pint

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.74.6

Branches

Tags

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

Clone

HTTPS

Download ZIP

internal/parser/models.go

426lines · modecode

1package parser
2
3import (
4 "fmt"
5 "slices"
6 "time"
7
8 "gopkg.in/yaml.v3"
9
10 "github.com/cloudflare/pint/internal/comments"
11 "github.com/cloudflare/pint/internal/diags"
12)
13
14func nodeValue(node *yaml.Node) string {
15 if node.Alias != nil {
16 return node.Alias.Value
17 }
18 return node.Value
19}
20
21func mergeComments(node *yaml.Node) (comments []string) {
22 if node.HeadComment != "" {
23 comments = append(comments, node.HeadComment)
24 }
25 if node.LineComment != "" {
26 comments = append(comments, node.LineComment)
27 }
28 if node.FootComment != "" {
29 comments = append(comments, node.FootComment)
30 }
31 for _, child := range node.Content {
32 comments = append(comments, mergeComments(child)...)
33 }
34 return comments
35}
36
37type YamlNode struct {
38 Value string
39 Pos diags.PositionRanges
40}
41
42func (yn *YamlNode) IsIdentical(b *YamlNode) bool {
43 if (yn == nil) != (b == nil) {
44 return false
45 }
46 if yn == nil {
47 return true
48 }
49 if yn.Value != b.Value {
50 return false
51 }
52 return true
53}
54
55func newYamlNode(node *yaml.Node, offsetLine, offsetColumn int, contentLines []string, minColumn int) *YamlNode {
56 pos := diags.NewPositionRange(contentLines, node, minColumn)
57 pos.AddOffset(offsetLine, offsetColumn)
58 return &YamlNode{
59 Pos: pos,
60 Value: nodeValue(node),
61 }
62}
63
64type YamlKeyValue struct {
65 Key *YamlNode
66 Value *YamlNode
67}
68
69type YamlMap struct {
70 Key *YamlNode
71 Items []*YamlKeyValue
72}
73
74func (ym *YamlMap) IsIdentical(b *YamlMap) bool {
75 var aItems, bItems []*YamlKeyValue
76 if ym != nil {
77 aItems = ym.Items
78 }
79 if b != nil {
80 bItems = b.Items
81 }
82 if len(aItems) != len(bItems) {
83 return false
84 }
85
86 for _, ai := range aItems {
87 var found bool
88 for _, bi := range bItems {
89 if ai.Key.Value == bi.Key.Value {
90 if ai.Value.Value != bi.Value.Value {
91 return false
92 }
93 found = true
94 break
95 }
96 }
97 if !found {
98 return false
99 }
100 }
101
102 return true
103}
104
105func (ym YamlMap) GetValue(key string) *YamlNode {
106 for _, child := range ym.Items {
107 if child.Key.Value == key {
108 return child.Value
109 }
110 }
111 return nil
112}
113
114func (ym *YamlMap) setValue(item *YamlKeyValue) {
115 for i := range ym.Items {
116 if ym.Items[i].Key.Value == item.Key.Value {
117 ym.Items[i].Value = item.Value
118 return
119 }
120 }
121 ym.Items = append(ym.Items, item)
122}
123
124func (ym YamlMap) Lines() (lr diags.LineRange) {
125 lr = ym.Key.Pos.Lines()
126 for _, item := range ym.Items {
127 lr.First = min(lr.First, item.Key.Pos.Lines().First)
128 lr.Last = max(lr.Last, item.Value.Pos.Lines().Last)
129 }
130 return lr
131}
132
133func newYamlMap(key, value *yaml.Node, offsetLine, offsetColumn int, contentLines []string) *YamlMap {
134 ym := YamlMap{
135 Key: newYamlNode(key, offsetLine, offsetColumn, contentLines, 1),
136 Items: nil,
137 }
138
139 var ckey *yaml.Node
140 for _, child := range value.Content {
141 if ckey != nil {
142 kv := YamlKeyValue{
143 Key: newYamlNode(ckey, offsetLine, offsetColumn, contentLines, key.Column+2),
144 Value: newYamlNode(child, offsetLine, offsetColumn, contentLines, ckey.Column+2),
145 }
146 ym.Items = append(ym.Items, &kv)
147 ckey = nil
148 } else {
149 ckey = child
150 }
151 }
152
153 return &ym
154}
155
156func (pqle PromQLExpr) IsIdentical(b PromQLExpr) bool {
157 return pqle.Value.Value == b.Value.Value
158}
159
160func newPromQLExpr(node *yaml.Node, offsetLine, offsetColumn int, contentLines []string, minColumn int) *PromQLExpr {
161 expr := PromQLExpr{
162 Value: newYamlNode(node, offsetLine, offsetColumn, contentLines, minColumn),
163 SyntaxError: nil,
164 Query: nil,
165 }
166
167 qlNode, err := DecodeExpr(expr.Value.Value)
168 if err != nil {
169 expr.SyntaxError = err
170 return &expr
171
172 }
173 expr.Query = qlNode
174 return &expr
175}
176
177type AlertingRule struct {
178 Expr PromQLExpr
179 For *YamlNode
180 KeepFiringFor *YamlNode
181 Labels *YamlMap
182 Annotations *YamlMap
183 Alert YamlNode
184}
185
186func (ar *AlertingRule) IsIdentical(b *AlertingRule) bool {
187 if (ar == nil) != (b == nil) {
188 return false
189 }
190 if ar == nil {
191 return true
192 }
193 if !ar.Alert.IsIdentical(&b.Alert) {
194 return false
195 }
196 if !ar.Expr.IsIdentical(b.Expr) {
197 return false
198 }
199 if !ar.For.IsIdentical(b.For) {
200 return false
201 }
202 if !ar.KeepFiringFor.IsIdentical(b.KeepFiringFor) {
203 return false
204 }
205 if !ar.Labels.IsIdentical(b.Labels) {
206 return false
207 }
208 if !ar.Annotations.IsIdentical(b.Annotations) {
209 return false
210 }
211 return true
212}
213
214type RecordingRule struct {
215 Expr PromQLExpr
216 Labels *YamlMap
217 Record YamlNode
218}
219
220func (rr *RecordingRule) IsIdentical(b *RecordingRule) bool {
221 if (rr == nil) != (b == nil) {
222 return false
223 }
224 if rr == nil {
225 return true
226 }
227 if !rr.Record.IsIdentical(&b.Record) {
228 return false
229 }
230 if !rr.Expr.IsIdentical(b.Expr) {
231 return false
232 }
233 if !rr.Labels.IsIdentical(b.Labels) {
234 return false
235 }
236 return true
237}
238
239// Use insread of StrictError.
240type ParseError struct {
241 Err error
242 Details string
243 Line int
244}
245
246func (pe ParseError) Error() string {
247 return fmt.Sprintf("error at line %d: %s", pe.Line, pe.Err)
248}
249
250type File struct {
251 Diagnostics []diags.Diagnostic
252 Comments []comments.Comment
253 Groups []Group
254 Error ParseError
255 TotalLines int
256 IsRelaxed bool
257 IsIgnored bool
258}
259
260type Group struct {
261 Labels *YamlMap
262 Name string
263 Error ParseError
264 Rules []Rule
265 Interval time.Duration
266 QueryOffset time.Duration
267 Limit int
268}
269
270type Rule struct {
271 AlertingRule *AlertingRule
272 RecordingRule *RecordingRule
273 Comments []comments.Comment
274 Error ParseError
275 Lines diags.LineRange
276}
277
278func (r Rule) IsIdentical(b Rule) bool {
279 if r.Type() != b.Type() {
280 return false
281 }
282 if !r.AlertingRule.IsIdentical(b.AlertingRule) {
283 return false
284 }
285 if !r.RecordingRule.IsIdentical(b.RecordingRule) {
286 return false
287 }
288
289 ac := make([]string, 0, len(r.Comments))
290 for _, c := range r.Comments {
291 ac = append(ac, c.Value.String())
292 }
293 slices.Sort(ac)
294
295 bc := make([]string, 0, len(r.Comments))
296 for _, c := range b.Comments {
297 bc = append(bc, c.Value.String())
298 }
299 slices.Sort(bc)
300
301 return slices.Equal(ac, bc)
302}
303
304func (r Rule) IsSame(nr Rule) bool {
305 if (r.AlertingRule != nil) != (nr.AlertingRule != nil) {
306 return false
307 }
308 if (r.RecordingRule != nil) != (nr.RecordingRule != nil) {
309 return false
310 }
311 if r.Error != nr.Error {
312 return false
313 }
314 if r.Lines.First != nr.Lines.First {
315 return false
316 }
317 if r.Lines.Last != nr.Lines.Last {
318 return false
319 }
320 return true
321}
322
323func (r Rule) Name() string {
324 if r.RecordingRule != nil {
325 return r.RecordingRule.Record.Value
326 }
327 if r.AlertingRule != nil {
328 return r.AlertingRule.Alert.Value
329 }
330 return ""
331}
332
333func (r Rule) NameNode() YamlNode {
334 if r.RecordingRule != nil {
335 return r.RecordingRule.Record
336 }
337 return r.AlertingRule.Alert
338}
339
340func (r Rule) Expr() PromQLExpr {
341 if r.RecordingRule != nil {
342 return r.RecordingRule.Expr
343 }
344 return r.AlertingRule.Expr
345}
346
347func (r Rule) LastKey() (node *YamlNode) {
348 if r.RecordingRule != nil {
349 node = &r.RecordingRule.Record
350 if r.RecordingRule.Expr.Value.Pos.Lines().Last > node.Pos.Lines().Last {
351 node = r.RecordingRule.Expr.Value
352 }
353 if r.RecordingRule.Labels != nil {
354 for _, lab := range r.RecordingRule.Labels.Items {
355 if lab.Key.Pos.Lines().Last > node.Pos.Lines().Last {
356 node = lab.Key
357 }
358 }
359 }
360 }
361 if r.AlertingRule != nil {
362 node = &r.AlertingRule.Alert
363 if r.AlertingRule.Expr.Value.Pos.Lines().Last > node.Pos.Lines().Last {
364 node = r.AlertingRule.Expr.Value
365 }
366 if r.AlertingRule.For != nil && r.AlertingRule.For.Pos.Lines().Last > node.Pos.Lines().Last {
367 node = r.AlertingRule.For
368 }
369 if r.AlertingRule.KeepFiringFor != nil && r.AlertingRule.KeepFiringFor.Pos.Lines().Last > node.Pos.Lines().Last {
370 node = r.AlertingRule.KeepFiringFor
371 }
372 if r.AlertingRule.Labels != nil {
373 for _, lab := range r.AlertingRule.Labels.Items {
374 if lab.Key.Pos.Lines().Last > node.Pos.Lines().Last {
375 node = lab.Key
376 }
377 }
378 }
379 if r.AlertingRule.Annotations != nil {
380 for _, ann := range r.AlertingRule.Annotations.Items {
381 if ann.Key.Pos.Lines().Last > node.Pos.Lines().Last {
382 node = ann.Key
383 }
384 }
385 }
386 }
387 return node
388}
389
390type RuleType string
391
392const (
393 AlertingRuleType RuleType = "alerting"
394 RecordingRuleType RuleType = "recording"
395 InvalidRuleType RuleType = "invalid"
396)
397
398func (r Rule) Type() RuleType {
399 if r.AlertingRule != nil {
400 return AlertingRuleType
401 }
402 if r.RecordingRule != nil {
403 return RecordingRuleType
404 }
405 return InvalidRuleType
406}
407
408func MergeMaps(a, b *YamlMap) *YamlMap {
409 if a == nil {
410 return b
411 }
412 if b == nil {
413 return a
414 }
415
416 dst := &YamlMap{
417 Key: a.Key,
418 Items: slices.Clone(a.Items),
419 }
420
421 for _, item := range b.Items {
422 dst.setValue(item)
423 }
424
425 return dst
426}
427