137 lines
2.7 KiB
Go
137 lines
2.7 KiB
Go
|
package cuetils
|
||
|
|
||
|
import (
|
||
|
"cuelang.org/go/cue"
|
||
|
)
|
||
|
|
||
|
// ScanForInputs walks a Value looking for potential inputs
|
||
|
// - non-concrete values or values with defaults
|
||
|
// - exclude @dagger(computed) and #up
|
||
|
// - exclude values which have references
|
||
|
func ScanForInputs(value cue.Value) ([]cue.Value, error) {
|
||
|
var (
|
||
|
vals []cue.Value
|
||
|
err error
|
||
|
)
|
||
|
|
||
|
// walk before function, bool return is if the walk should recurse again
|
||
|
before := func(v cue.Value) (bool, error) {
|
||
|
// explicit phase
|
||
|
// look for #up
|
||
|
label, _ := v.Label()
|
||
|
if label == "#up" {
|
||
|
return false, nil
|
||
|
}
|
||
|
|
||
|
// look to exclude any @dagger(computed)
|
||
|
attrs := v.Attributes(cue.ValueAttr)
|
||
|
for _, attr := range attrs {
|
||
|
name := attr.Name()
|
||
|
// match `@dagger(...)`
|
||
|
if name == "dagger" {
|
||
|
// loop over args (CSV content in attribute)
|
||
|
for i := 0; i < attr.NumArgs(); i++ {
|
||
|
key, _ := attr.Arg(i)
|
||
|
|
||
|
// we found an explicit computed value
|
||
|
if key == "computed" {
|
||
|
return false, nil
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// inference phase
|
||
|
switch v.IncompleteKind() {
|
||
|
case cue.StructKind:
|
||
|
return true, nil
|
||
|
|
||
|
case cue.ListKind:
|
||
|
if !v.IsConcrete() {
|
||
|
vals = append(vals, v)
|
||
|
return false, nil
|
||
|
}
|
||
|
return true, nil
|
||
|
|
||
|
default:
|
||
|
|
||
|
// a leaf with default?
|
||
|
_, has := v.Default()
|
||
|
if has {
|
||
|
vals = append(vals, v)
|
||
|
// recurse here?
|
||
|
return false, nil
|
||
|
}
|
||
|
|
||
|
// is this leaf not concrete? (should cause an error)
|
||
|
if v.Validate(cue.Concrete(true), cue.Optional(true)) != nil {
|
||
|
vals = append(vals, v)
|
||
|
}
|
||
|
|
||
|
return false, nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// walk
|
||
|
err = walkValue(value, before, nil)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
return vals, nil
|
||
|
}
|
||
|
|
||
|
// walkValue is a custome walk function so that we recurse into more types than CUE's buildin walk
|
||
|
// specificially, we need to customize the options to val.Fields when val is a struct
|
||
|
func walkValue(val cue.Value, before func(cue.Value) (bool, error), after func(cue.Value) error) error {
|
||
|
if before != nil {
|
||
|
recurse, err := before(val)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
// should we recurse into fields
|
||
|
if recurse {
|
||
|
switch val.IncompleteKind() {
|
||
|
case cue.StructKind:
|
||
|
// provide custom args to ensure we walk nested defs
|
||
|
// and that optionals are included
|
||
|
iter, err := val.Fields(
|
||
|
cue.Definitions(true),
|
||
|
cue.Optional(true),
|
||
|
)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
for iter.Next() {
|
||
|
err := walkValue(iter.Value(), before, after)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
|
||
|
case cue.ListKind:
|
||
|
iter, err := val.List()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
for iter.Next() {
|
||
|
err := walkValue(iter.Value(), before, after)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if after != nil {
|
||
|
err := after(val)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|