§ Learn · Track A · Companions

CUE cheat sheet

Every construct of Track A on one page. Each has a short example and the output that pravo prints.

Time
5 min
Level
beginner

View as Markdown

On this page

Each section holds one file and the output of pravo eval. Work in an empty directory. Each file replaces the previous estate.cue. The lessons explain the reasons. This page only shows the shapes.

Symbols

Write Name Lesson
a: 1 field A1
a: b: 1 nested fields in one line A1
int, string, _ a type is a value A2
a: int then a: 3 unification A3
& combine two values A5
>=1, <10, !=0 bound A5
=~"re", !~"re" regular expression match A5
a | b disjunction, a choice A6
*a | b default A6
@tag(env) tag, set with -t env=prod A6
a!: int required field A7
a?: int optional field A7
a: int regular field A7
#Name: {...} definition, a closed shape A8
{a: int, ...} an open struct A8
{#Other, a: int} embedding A8
[string]: T pattern A9
for k, v in x {...} comprehension A10
a: b reference A11
let x = 1 let A11
_x: 1 hidden field A11
if c {...} a condition A12
"\(x)" interpolation A12
package p, import "x" package and import A13

Data

A struct holds fields. A list holds values. A string, a number, a bool and null are the scalars. A1

estate.cue
package estate

tags:  {owner: "platform"} // a struct
ports: [80, 443]           // a list
a: b: c: 1                 // nested fields in one line
who:   tags.owner          // select a field
first: ports[0]            // index a list

name:  "logs"              // a string
size:  3                   // a number
ratio: 0.5
ready: true                // a bool
none:  null
terminal
$pravo eval
tags: {
	owner: "platform"
}
ports: [80, 443]
a: {
	b: {
		c: 1
	}
}
who:   "platform"
first: 80
name:  "logs"
size:  3
ratio: 0.5
ready: true
none:  null

Types and constraints

A type is a value. & combines values. A bound or a match is a constraint that narrows a type. A2, A5

estate.cue
package estate

count:    int                      // a type is a value
label:    string
ready:    bool
price:    number
hosts:    [...string] & ["a", "b"] // a list of strings
anything: _                        // any value

port: int & >=1 & <=65535 & 8080   // bounds narrow a type
code: =~"^[a-z]{3}$" & "abc"       // a match narrows a string
text: !~"^[0-9]+$" & "abc"         // a non-match
rest: !=0 & 7                      // an inequality
terminal
$pravo eval
count: int
label: string
ready: bool
price: number
hosts: ["a", "b"]
anything: _
port:     8080
code:     "abc"
text:     "abc"
rest:     7

A field that holds a type is not concrete. It is incomplete until a value narrows it.

Writing a field twice

Copies of a field combine. The more specific value wins. Structs merge. Order and file do not matter. A3, A4

estate.cue
package estate

size: int                // copies combine
size: 3                  // the more specific value wins

tags: owner: "platform"  // structs merge
tags: team:  "infra"
terminal
$pravo eval
size: 3
tags: {
	owner: "platform"
	team:  "infra"
}

Two different concrete values are a conflict. The result is bottom: no value satisfies both, and pravo prints an error.

Choices and defaults

| offers choices. * marks the default. @tag takes its value from the command line. A6

estate.cue
package estate

class: "standard" | "ia"           // a choice, undecided
tier:  *"standard" | "ia"          // a choice with a default
env:   *"dev" | "prod" @tag(env)   // a tag
terminal
$pravo eval
$pravo eval -t env=prod -e env
class: "standard" | "ia"
tier:  "standard"
env:   "dev"
"prod"

Required, optional, regular

A required field must be supplied. An optional field may stay unset. A regular field is a field. A7

estate.cue
package estate

bucket: {
	name:   string   // regular
	owner!: string   // required
	note?:  string   // optional
}
terminal
$pravo eval
$pravo eval -O
bucket: {
	name:   string
	owner!: string
}
bucket: {
	name:   string
	owner!: string
	note?:  string
}

Definitions

A definition starts with #. It is closed: a value built from it may hold only declared fields. ... opens it. Embedding includes the fields of another definition. A8

estate.cue
package estate

#Bucket: {name!: string, versioning: *false | bool}   // a closed shape
logs:    #Bucket & {name: "logs"}

#Loose: {name: string, ...}                           // an open shape
extra:  #Loose & {name: "x", anything: 1}

#Tagged: {owner!: string}
#Owned:  {#Tagged, name!: string}                     // embedding
owned:   #Owned & {name: "n", owner: "me"}
terminal
$pravo eval
logs: {
	name:       "logs"
	versioning: false
}
extra: {
	name:     "x"
	anything: 1
}
owned: {
	name:  "n"
	owner: "me"
}

pravo eval omits definitions. Name one with -e '#Bucket'.

Patterns

A pattern applies a rule to every field of a struct, now or added later. Name= names the key. A9

estate.cue
package estate

buckets: [string]: {size: *1 | int}   // a rule for every entry
buckets: logs: {}

nodes: [Name=string]: {name: Name}    // Name stands for the key
nodes: web: {}

labels: [string]: string              // a map of strings
labels: owner: "platform"
terminal
$pravo eval
buckets: {
	logs: {
		size: 1
	}
}
nodes: {
	web: {
		name: "web"
	}
}
labels: {
	owner: "platform"
}

A pattern constrains entries that exist. It does not create them.

Comprehensions

for builds fields or list items from other data. if filters. A10

estate.cue
package estate

zones: {a: 1, b: 2}

subnets: {for z, n in zones {"net-\(z)": n * 10}}   // fields from a struct
doubled: [for n in [1, 2, 3] {n * 2}]              // a list from a list
big:     [for n in [1, 2, 3] if n > 1 {n}]         // with a filter
terminal
$pravo eval
zones: {
	a: 1
	b: 2
}
subnets: {
	"net-a": 10
	"net-b": 20
}
doubled: [2, 4, 6]
big: [2, 3]

The key you build is the identity of the entry. Choose keys that do not move.

Names

A field reads another by name. A let and a hidden field name a helper and stay out of the output. A11

estate.cue
package estate

base: "10.0"
cidr: "\(base).0.0/16"      // a reference

let prefix = "estate"       // a let: not printed
_zone:  "eu-west-1"         // a hidden field: not printed
name:   "\(prefix)-logs"
region: _zone
terminal
$pravo eval
base:   "10.0"
cidr:   "10.0.0.0/16"
name:   "estate-logs"
region: "eu-west-1"

A field must not share a name with the value it reads. region: region refers to itself.

Conditions and strings

if includes fields when its condition is true. There is no else. \( ) puts a value into a string. A struct indexed by a key looks a value up. A12

estate.cue
package estate

env: *"dev" | "prod" @tag(env)

retention: *7 | int
if env == "prod" {retention: 90}   // a condition

name: "estate-\(env)"              // interpolation
full: name + "-bucket"             // join strings
days: {dev: 7, prod: 90}[env]      // a lookup
note: """
	one
	two
	"""
terminal
$pravo eval
$pravo eval -t env=prod
env:       "dev"
retention: 7
name:      "estate-dev"
full:      "estate-dev-bucket"
days:      7
note: """
	one
	two
	"""
retention: 90
env:       "prod"
name:      "estate-prod"
full:      "estate-prod-bucket"
days:      90
note: """
	one
	two
	"""

Packages and the standard library

A directory is a package. import brings in another one. Validators join a value with &. Functions compute a value. A13

estate.cue
package estate

import (
	"encoding/json"
	"list"
	"net"
	"strings"
)

cidr:  net.IPCIDR & "10.0.1.0/24"   // a validator
zones: list.MinItems(1) & ["a"]    // a validator

up:  strings.ToUpper("logs")       // a function
doc: json.Marshal({a: 1})
n:   len(zones)
terminal
$pravo eval
cidr: "10.0.1.0/24"
zones: ["a"]
up:  "LOGS"
doc: "{\"a\":1}"
n:   1

Your own package is imported by module path and directory.

estate.cue
import "estate.example/platform"

subnet: platform.#Subnet & {zone: "a", cidr: "10.0.1.0/24"}

Commands

Command Does
pravo eval Prints the evaluated specification.
pravo eval -e path Prints one path or expression.
pravo eval -c Fails unless every value is concrete.
pravo eval -O Also prints optional fields.
pravo eval -t env=prod Sets a tag.
pravo validate Checks the specification against provider schemas.
pravo fmt Rewrites the .cue files of the directory in the standard layout.

C4 explains each flag.

estate.cue
package estate
size:int
tags:   {owner:"platform",team:"infra"}
name:    "logs"
terminal
$pravo fmt
$cat estate.cue
estate.cue
package estate

size: int
tags: {owner: "platform", team: "infra"}
name: "logs"

Errors

An error names the path, the reason and the places that took part. C1 reads one line by line. The error catalogue lists the errors of pravo itself.

Message Meaning Lesson
conflicting values 4 and 3 Two concrete values for one field. A3
invalid value 70000 (out of bound <=65535) A value outside a bound. A5
3 errors in empty disjunction No choice matches the value. A6
field is required but not present A field marked ! has no value. A7
field not allowed A closed definition has no such field. A8
reference "bse" not found No field or let of that name is in reach. A11
unreferenced alias or let clause x A let that nothing reads. A11
non-concrete value string in operand to == A condition on a value that is still a type. A12
imported and not used: "strings" An import that nothing uses. A13
does not satisfy net.IPCIDR A validator refused the value. A13
incomplete value string With -c: the field is still a type. C2
no tag for "stage" -t names a tag that no field declares. C4