§ A7 · Track A · CUE from zero
Required, optional, regular
Decide for each field whether it must be supplied, may be supplied, or is a plain field.
On this page
HCL answers "what if nobody sets it?" with three different mechanisms.
variable "bucket" {
type = object({
name = string
description = optional(string)
versioning = optional(bool, false)
})
}
An attribute without optional() must be supplied. An omitted description becomes null. An omitted versioning becomes false.
In CUE, a field is one of three kinds. A regular field has no mark. A field written name! is a required field: someone must supply a value. A field written name? is an optional field: it may stay absent.
Warm-up
From earlier lessons
A bucket name must match =~"^[a-z0-9-]{3,63}$", and a second line sets it to "Logs". Is the value accepted?
No. The capital letter does not match the regular expression (A5).
A field is name: string and nothing else is written. Does pravo eval fail?
No. A type is a value that is not finished yet (A2). The flag -c asks for finished values and fails.
One bucket, three kinds
Work in an empty directory. Write estate.cue. Each kind appears once, and no field has a value.
package estate
bucket: {
versioning: bool
name!: string
description?: string
}
Decide for each field, then open the answer.
Predict Which of the three fields does pravo eval print?
Two. The plain field and the required field are printed. The optional field is left out.
bucket: {
versioning: bool
name!: string
}
The optional field exists in the specification. pravo eval hides it while it has no value. The flag -O prints it.
bucket: {
versioning: bool
name!: string
description?: string
}
The marks are part of the output. name! and description? keep their marks until a value arrives.
Break it
Ask for finished values with -c.
Error: the value is not concrete:
bucket.name: field is required but not present:
./estate.cue:5:2
bucket.versioning: incomplete value bool:
./estate.cue:4:16
Two fields are reported. The optional field is not.
bucket.name: field is required but not presentis the required field. The location is the line that declaresname!. It marks the demand, not the place where the value belongs.bucket.versioning: incomplete value boolis the plain field. It is still a type (A2).description?is absent. An absent optional field is not an error.
Here is the table that answers the question for all three kinds.
| Never set | Plain | Required | Optional |
|---|---|---|---|
| Written as | x: string |
x!: string |
x?: string |
pravo eval prints |
x: string |
x!: string |
nothing |
pravo eval -O prints |
x: string |
x!: string |
x?: string |
pravo eval -c |
fails: incomplete value | fails: required but not present | no error |
Once a value is set, all three kinds print x: "a". C2 lists every cause of an incomplete value.
Supply the values
Write a value for each field that needs one.
package estate
bucket: {
versioning: bool
name!: string
description?: string
}
bucket: {
name: "logs"
versioning: true
}
bucket: {
versioning: true
name: "logs"
}
bucket: {
versioning: true
name: "logs"
description?: string
}
The flag -c passes. The optional field stays absent, because nobody set it. Writing description: "Access logs" makes it appear in pravo eval.
Which kind to write
| You want | Write | In HCL |
|---|---|---|
| The caller must supply it | name!: string |
an attribute without optional() |
| The caller may supply it | description?: string |
optional(string), which gives null |
| A value in every case | versioning: *false | bool |
optional(bool, false) |
The third row is a plain field with a default (A6). It always has a value, so -c never fails on it.
Exercise
Require an owner
Replace estate.cue with this file. Both owner and description are plain fields, so -c reports both.
package estate
bucket: {
name: "logs"
owner: string
description: string
}
Error: the value is not concrete:
bucket.description: incomplete value string:
./estate.cue:6:15
bucket.owner: incomplete value string:
./estate.cue:5:15
Mark owner as required and description as optional. Then pravo eval -c fails only for owner, and it passes once owner has a value.
The check runs the file as it is, and again with an owner supplied from a second file.
tmp=$(mktemp -d); cp estate.cue "$tmp/"
printf 'package estate\n\nbucket: owner: "platform"\n' > "$tmp/fixture.cue"
pravo eval -c 2>&1 | grep -q 'owner: field is required but not present' || { echo "owner should be required"; exit 1; }
if pravo eval -c 2>&1 | grep -q 'description'; then echo "description should be optional"; exit 1; fi
pravo -C "$tmp" eval -c > /dev/null 2>&1 || { echo "with an owner, -c should pass"; exit 1; }
pravo -C "$tmp" eval -O | grep -q 'description?: string' || { echo "description should stay optional"; exit 1; }
rm -rf "$tmp"
echo ok
Run it on the start state. It fails.
owner should be required
Hint 1
Put ! after the field name for a required field. Put ? after the field name for an optional one. The colon stays.
Hint 2
See required field and optional field. Neither mark changes the type after the colon.
Solution
package estate
bucket: {
name: "logs"
owner!: string
description?: string
}
Error: the value is not concrete:
bucket.owner: field is required but not present:
./estate.cue:5:2
Recap
- A plain field may stay a type, and
-creports it as incomplete. - A required field
name!must be supplied, and-creports it if it is not. - An optional field
name?may stay absent, and-Oprints it.
Check yourself
Which fields does pravo eval -c report when nothing sets them?
Plain fields that are still types, and required fields. Optional fields are never reported.
Where does an optional field appear in pravo eval output when it has no value?
Nowhere. Only pravo eval -O prints it, as description?: string.
The next lesson makes a shape that refuses fields it does not declare.