cloudflare/pint

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.79.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

internal/parser/models.go

431lines · modecode

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