There are several ways to traverse values
from looking up based on path to iteration and walking the value tree.
Some of these are configurable, which is tied to the different labels
like regular, hidden, optional, and definitions.
We will use the following Value for the examples in this section.
We saw how to use LookupPath with ParsePath in a previous section.
We can programmatically construct paths with Selectors and MakePath.
We’ll also use this to reconstruct the label for the current value.
selectors.go
package main
import (
"fmt""os""cuelang.org/go/cue""cuelang.org/go/cue/cuecontext")
funcmain() {
c := cuecontext.New()
d, _ := os.ReadFile("value.cue")
val := c.CompileBytes(d)
// printing the label vs path
sub := val.LookupPath(cue.ParsePath("obj.sub"))
fmt.Println(sub.Path(), getLabel(sub))
}
// helper function for getting the label for a value
funcgetLabel(val cue.Value) cue.Selector {
ss := val.Path().Selectors()
s := ss[len(ss)-1]
return s
}
package main
import (
"fmt""os""cuelang.org/go/cue""cuelang.org/go/cue/cuecontext")
funcmain() {
c := cuecontext.New()
d, _ := os.ReadFile("value.cue")
val := c.CompileBytes(d)
// lookup a list value
val = val.LookupPath(cue.ParsePath("obj.list"))
// we use iterators to traverse a list
// List() returns an iterator
iter, _ := val.List()
// This pattern is standard iteration
// We get the current element and nil at the end
for iter.Next() {
fmt.Println(iter.Value())
}
}
In the previous example for default walk, some of the fields were not traversed.
This is because CUE’s default Walk() uses the same default Field() options on a value.
In order to walk all fields, we need to write a custom walk function
where we can pass in the options for Field().
custom.go
package main
import (
"fmt""os""cuelang.org/go/cue""cuelang.org/go/cue/cuecontext")
const input = `
a: {
i: int
j: int | *i
}
`funcmain() {
c := cuecontext.New()
d, _ := os.ReadFile("value.cue")
val := c.CompileBytes(d)
// before (pre-order) traversal
preprinter :=func(v cue.Value) bool {
fmt.Printf("%v\n", v)
returntrue }
// after (post-order) traversal
cnt :=0 postcounter :=func(v cue.Value) {
cnt++ }
Walk(val, preprinter, postcounter, customOptions...)
}
// Walk is an alternative to cue.Value.Walk which handles more field types
// You can customize this with your own options
funcWalk(v cue.Value, before func(cue.Value) bool, after func(cue.Value), options ...cue.Option) {
// call before and possibly stop recursion
if before !=nil&& !before(v) {
return }
// possibly recurse
switch v.IncompleteKind() {
case cue.StructKind:
if options ==nil {
options = defaultOptions
}
s, _ := v.Fields(options...)
for s.Next() {
Walk(s.Value(), before, after, options...)
}
case cue.ListKind:
l, _ := v.List()
for l.Next() {
Walk(l.Value(), before, after, options...)
}
// no default (basic lit types)
}
if after !=nil {
after(v)
}
}
// Cue's default
var defaultOptions = []cue.Option{
cue.Attributes(true),
cue.Concrete(false),
cue.Definitions(false),
cue.DisallowCycles(false),
cue.Docs(false),
cue.Hidden(false),
cue.Optional(false),
cue.ResolveReferences(false),
// The following are not set
// nor do they have a bool arg
// cue.Final(),
// cue.Raw(),
// cue.Schema(),
}
// Our custom options
var customOptions = []cue.Option{
cue.Definitions(true),
cue.Hidden(true),
cue.Optional(true),
}