Field Patterns



There are a number of patterns which allow us to add complexity to field constraints and validation.

OneOf

The OneOf pattern is used to enforce one and only one of a set of fields. We use disjunctions and an embedding to create this behavior.

oneOf.cue

#OneOf: {a: string} | {b: string}

#E: {
	name: string
	#OneOf
}

ex1: #E & {
	name: "a choice"
	a:    "bar"
}

ex2: #E & {
	name: "b choice"
	b:    "hello"
}

ex3: #E & {
	name: "error none chosen"
}

ex4: #E & {
	name: "error both chosen"
	a:    "a"
	b:    "b"
}

You can also have more options and an empty option if you want to allow none of them to be valid as well.

oneOfMaybe.cue

#OneOf: {} | {a: string} | {b: string} | {c: int} | {d: bool}

However, you cannot have options which are subsets of another option. The following will error when only a is provided:

subsetFail.cue

#OneOf: {a: _} | {a: _, b: _}

CUE cannot distinguish between the choices.

AnyOf

AnyOf is a pattern where we want at least one of a set of fields to be present.

AnyOf requires a validator field to check for the existence of optional fields. The pattern here makes use of

  • a set of optional fields that we want 1 or more of.
  • the alias this= so we can reference the local fields. You can use a different name than this.
  • a list comprehension over this, which uses list.Contains() for the fields to check.
  • a definition with true & list.MinItems to check that there is at least one of the labels.

anyOf.cue

import "list"

val: this={
	a?:        string
	b?:        string
	c?:        string
	#AnyOfABC: true & list.MinItems([ for label, _ in this if list.Contains(["a", "b", "c"], label) {label}], 1)

	d?:       string
	e?:       string
	#AnyOfDE: true & list.MinItems([ for label, _ in this if list.Contains(["d", "e"], label) {label}], 1)

	#AnyOfXYZ & {#CheckXYZ: true}

	a: "a"
	d: "d"
	z: "z"
}

#AnyOfXYZ: this={
	x?:        string
	y?:        string
	z?:        string
	#CheckXYZ: list.MinItems([ for label, _ in this if list.Contains(["x", "y", "z"], label) {label}], 1)
}

Note that we have two variations, local like ABC and DE, and definition based like XYZ. You can also change the minimum required fields if you so desire by adjusting the second argument to list.MinItems. It is also possible to have overlapping sets.

Note, this is a workaround until the required fields and builtins proposals provided a more native method. See cueology/futurology for more details on the proposals.

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