Inspecting Values



CUE values have a number of methods that we can use to inspect their contents, state, and location. While we saw how to print the full value and its error state, the functions here provide more granular details.

We will be using the following CUE file for the examples in this section.

value.cue

a: {
	b: "b"
	c: "c"
	e: int
}

d: {
	e: 42
	f: 3.14
	g: _
}

l: [1, 2, "y", "z"]
b: 'abc123'

r: s: d.e

LookupPath, Path

Path return paths to the value which can be used with LookupPath to get the value. They can be considered inverse functions of each other.

path.go

package main

import (
	"fmt"
	"os"

	"cuelang.org/go/cue"
	"cuelang.org/go/cue/cuecontext"
)

func main() {
	c := cuecontext.New()

	// read and compile value
	d, _ := os.ReadFile("value.cue")
	val := c.CompileBytes(d)

	paths := []string{
		"a",
		"d.f",
		"l",
	}

	for _, path := range paths {
		fmt.Printf("====  %s  ====\n", path)
		v := val.LookupPath(cue.ParsePath(path))
		p := v.Path()
		fmt.Printf("%q\n%# v\n", p, v)
	}
}

go run path.go

====  a  ====
"a"
b: "b"
c: "c"
e: int
====  d.f  ====
"d.f"
3.14
====  l  ====
"l"
[1, 2, "y", "z"]

Docs: LookupPath, Path

ReferencePath and Dereference

ReferencePath

reference.go

package main

import (
	"fmt"
	"os"

	"cuelang.org/go/cue"
	"cuelang.org/go/cue/cuecontext"
)

func main() {
	c := cuecontext.New()

	// read and compile value
	d, _ := os.ReadFile("value.cue")
	val := c.CompileBytes(d)

	paths := []string{
		"d.f",
		"r.s",
	}

	for _, path := range paths {
		fmt.Printf("====  %s  ====\n", path)
		v := val.LookupPath(cue.ParsePath(path))
		p := v.Path()
		_, r := v.ReferencePath()
		fmt.Printf("%q %q\n%# v\n", p, r, v)
	}
}

go run reference.go

====  d.f  ====
"d.f" ""
3.14
====  r.s  ====
"r.s" "d.e"
d.e

Docs: ReferencePath, Dereference

Exists and IsConcrete

When you lookup a value, how do you know if it was found? That is where Exists comes in.

IsConcrete can tell you if an atom field has data or is a terminal error. For lists and structs, it will report true if they exist and not recurse to check subvalues. When disjunctions and defaults are used…

exists.go

package main

import (
	"fmt"
	"os"

	"cuelang.org/go/cue"
	"cuelang.org/go/cue/cuecontext"
)

func main() {
	c := cuecontext.New()

	// read and compile value
	d, _ := os.ReadFile("value.cue")
	val := c.CompileBytes(d)

	paths := []string{
		"a",
		"d.g",
		"x.y",
	}

	for _, path := range paths {
		fmt.Printf("====  %s  ====\n", path)
		v := val.LookupPath(cue.ParsePath(path))
		p := v.Path()
		x := v.Exists()
		c := v.IsConcrete()
		fmt.Printf("%q %v %v\n%# v\n", p, x, c, v)
	}
}

go run exists.go

====  a  ====
"a" true true
b: "b"
c: "c"
e: int
====  d.g  ====
"d.g" true false
_
====  x.y  ====
"" false true
_|_ // field "x" not found

Use Validate to check complete values for concreteness.

Docs: Exists, IsConcrete

Kind and IncompleteKind

Kind and IncompleteKind will tell you the underlying type of a value. IncompleteKind is more granular and returns type info regarless of how complete a value is (the names may seem a bit backwards).

kind.go

package main

import (
	"fmt"
	"os"

	"cuelang.org/go/cue"
	"cuelang.org/go/cue/cuecontext"
)

func main() {
	c := cuecontext.New()

	// read and compile value
	d, _ := os.ReadFile("value.cue")
	val := c.CompileBytes(d)

	paths := []string{
		"a",
		"a.e",
		"d.g",
		"l",
		"b",
		"x.y",
	}

	for _, path := range paths {
		v := val.LookupPath(cue.ParsePath(path))
		k := v.Kind()
		i := v.IncompleteKind()
		fmt.Printf("%q %v %v %v\n", path, k, i, v)
	}
}

go run kind.go

"a" struct struct {
	b: "b"
	c: "c"
	e: int
}
"a.e" _|_ int int
"d.g" _|_ _ _
"l" list list [1, 2, "y", "z"]
"b" bytes bytes 'abc123'
"x.y" _|_ _|_ _|_ // field "x" not found

You can also switch on the results of both functions.

Docs: Kinds, Kind, IncompleteKind

Type Conversions

Values have a number of functions for turning the abstract into the underlying type. You will first want to know what type of value you are dealing with before trying to convert it.

conversions.go

package main

import (
	"fmt"
	"os"

	"cuelang.org/go/cue"
	"cuelang.org/go/cue/cuecontext"
)

func main() {
	c := cuecontext.New()

	// read and compile value
	d, _ := os.ReadFile("value.cue")
	val := c.CompileBytes(d)

	var (
		s string
		b []byte
		i int64
		f float64
	)

	// read into Go basic types
	s, _ = val.LookupPath(cue.ParsePath("a.b")).String()
	b, _ = val.LookupPath(cue.ParsePath("b")).Bytes()
	i, _ = val.LookupPath(cue.ParsePath("d.e")).Int64()
	f, _ = val.LookupPath(cue.ParsePath("d.f")).Float64()

	fmt.Println(s, b, i, f)

	// an error
	s, err := val.LookupPath(cue.ParsePath("a.e")).String()
	if err != nil {
		fmt.Println(err)
	}

}

go run conversions.go

b [97 98 99 49 50 51] 42 3.14
a.e: cannot use value int (type int) as string

Len

Len will tell you the length of a list or how many bytes are in a bytes.

length.go

package main

import (
	"fmt"
	"os"

	"cuelang.org/go/cue"
	"cuelang.org/go/cue/cuecontext"
)

func main() {
	c := cuecontext.New()

	// read and compile value
	d, _ := os.ReadFile("value.cue")
	val := c.CompileBytes(d)

	paths := []string{
		"a",
		"d.e",
		"d.g",
		"l",
		"b",
	}

	for _, path := range paths {
		fmt.Printf("====  %s  ====\n", path)
		v := val.LookupPath(cue.ParsePath(path))
		p := v.Path()
		k := v.IncompleteKind()
		l := v.Len()
		fmt.Printf("%q %v %v\n%# v\n", p, k, l, v)
	}
}

go run length.go

====  a  ====
"a" struct _|_ // len not supported for type struct
b: "b"
c: "c"
e: int
====  d.g  ====
"d.g" _ _|_ // len not supported for type _|_
_
====  l  ====
"l" list 4
[1, 2, "y", "z"]
====  b  ====
"b" bytes 6
'abc123'

Docs: Len

We'll never share your email with anyone else.
2024 Hofstadter, Inc