grafana/pkg/kindsysreport/attributes.go
Kristin Laemmert 8745d7ef1b
extract kindsys to external library (#64562)
* extract kindsys

* reinstate kindsys report

This may end up living somewhere else (or not! who knows!), but the important part is that I don't get rid of it right now :)

I hate the package layout (kindsysreport/codegen) for the main function and will take pretty much any alternative suggestion, but we can change also change it later.

Note that the generated report.json is in a different location - anything using this (ops something) needs to be updated.

* kindsysreport in codeowners
2023-03-15 12:04:28 -04:00

75 lines
1.5 KiB
Go

package kindsysreport
import (
"cuelang.org/go/cue"
)
type AttributeWalker struct {
seen map[cue.Value]bool
count map[string]int
}
func (w *AttributeWalker) Count(sch cue.Value, attrs ...string) map[string]int {
w.seen = make(map[cue.Value]bool)
w.count = make(map[string]int)
for _, attr := range attrs {
w.count[attr] = 0
}
w.walk(cue.MakePath(), sch)
return w.count
}
func (w *AttributeWalker) walk(p cue.Path, v cue.Value) {
if w.seen[v] {
return
}
w.seen[v] = true
for attr := range w.count {
if found := v.Attribute(attr); found.Err() == nil {
w.count[attr]++
}
}
// nolint: exhaustive
switch v.Kind() {
case cue.StructKind:
// If current cue.Value is a reference to another
// definition, we don't want to traverse its fields
// individually, because we'll do so for the actual def.
if v != cue.Dereference(v) {
return
}
iter, err := v.Fields(cue.All())
if err != nil {
panic(err)
}
for iter.Next() {
w.walk(appendPath(p, iter.Selector()), iter.Value())
}
if lv := v.LookupPath(cue.MakePath(cue.AnyString)); lv.Exists() {
w.walk(appendPath(p, cue.AnyString), lv)
}
case cue.ListKind:
list, err := v.List()
if err != nil {
panic(err)
}
for i := 0; list.Next(); i++ {
w.walk(appendPath(p, cue.Index(i)), list.Value())
}
if lv := v.LookupPath(cue.MakePath(cue.AnyIndex)); lv.Exists() {
w.walk(appendPath(p, cue.AnyString), lv)
}
}
}
func appendPath(p cue.Path, sel cue.Selector) cue.Path {
return cue.MakePath(append(p.Selectors(), sel)...)
}