Generate Configuration



Cue is partially inspired by functional languages and has some features which help us to write “code” which generates large amounts of config or data. When you combine comprehension with conjunctions, defaults, and guards, they will all become invaluable assets to your Cue toolbox.

You can find more examples in the patterns section.

List and Field Comprehension

List and field comprehensions allow us to iterate over an iterable and generate new lists or fields. An iterable is an existing list or the fields of a struct.

import "strings"

band: {
	name: "Led Zeppelin"

	// List comprehension
	selfTitled: [ for i, I in _selfIndexAlbums { title: "\(name) \(I)" } ]
	// nested list comprehension
	allAlbums: [
		for I in _selfIndexAlbums { title: "\(name) \(I)" },
		for N in _namedAlbums { title: "\(N)" },
	]

	Albums: {
		// Field comprehension 
		for key, val in allAlbums {
			"\(strings.TrimSpace(val.title))": {
				pos: key
				artist: name
				title: strings.TrimSpace(val.title)
				titleLen: len(val.title)
			}
		}
	}

	// Hidden fields
	_selfIndexAlbums: ["", "II", "III", "IV"]
	_namedAlbums: [
		"Houses of the Holy",
		"Physical Graffiti",
		"Presence",
		"In Through the Out Door",
		"Coda"
	]
}

We’ve introducted a few things in this file:

  • list comprehension: [ for key, val in iterable { ... } ]. Notice you can leave the key off and you just get the value.
  • field comprehension: [ for key, val in iterable { ... } ]. We’ve interoplated a field name by calling a builtin, notice the surronding quotes.
  • string interpolation: "\(<a cue expression)". These are based on Swift’s string interpolation and s the only valid mechanism when you want to be JSON compatible.
  • hidden fields: _hidden: "I'm hidden" which begin with an underscore. There are also hidden definitions (_#)
band:
  name: Led Zeppelin
  selfTitled:
  - title: 'Led Zeppelin '
  - title: Led Zeppelin II
  - title: Led Zeppelin III
  - title: Led Zeppelin IV
  allAlbums:
  - title: 'Led Zeppelin '
  - title: Led Zeppelin II
  - title: Led Zeppelin III
  - title: Led Zeppelin IV
  - title: Houses of the Holy
  - title: Physical Graffiti
  - title: Presence
  - title: In Through the Out Door
  - title: Coda
  Albums:
    Led Zeppelin:
      pos: 0
      artist: Led Zeppelin
      title: Led Zeppelin
      titleLen: 13
    Led Zeppelin II:
      pos: 1
      artist: Led Zeppelin
      title: Led Zeppelin II
      titleLen: 15
    Led Zeppelin III:
      pos: 2
      artist: Led Zeppelin
      title: Led Zeppelin III
      titleLen: 16
    Led Zeppelin IV:
      pos: 3
      artist: Led Zeppelin
      title: Led Zeppelin IV
      titleLen: 15
    Houses of the Holy:
      pos: 4
      artist: Led Zeppelin
      title: Houses of the Holy
      titleLen: 18
    Physical Graffiti:
      pos: 5
      artist: Led Zeppelin
      title: Physical Graffiti
      titleLen: 17
    Presence:
      pos: 6
      artist: Led Zeppelin
      title: Presence
      titleLen: 8
    In Through the Out Door:
      pos: 7
      artist: Led Zeppelin
      title: In Through the Out Door
      titleLen: 23
    Coda:
      pos: 8
      artist: Led Zeppelin
      title: Coda
      titleLen: 4


Conjunctions and Defaults

We’ve already seen conjunctions being used for validation. You can also use them to build up Cue values and your configuration.

#labels: [string]: string

#metadata: {
	name: string
	// we can explicitly prevent namespace by commenting it out and ensuring this definition is closed
	// namespace?: string
	labels: #labels
	annotations?: [string]: string
}

// require the following keys for labels
#requiredLabels: #labels & {
	app: string
	env: string
	team: string
}

// for env, limit the choices and default to dev with '*'
#defaultLabels: #requiredLabels & {
	env: *"dev" | "stg" | "prd"
}

// set concrete values to be reused
#myLabels: #defaultLabels & {
	app: "cuetorials"
	team: "hofstadter"
}

// our Kubernetes definitions from before
#Schema: #Deployment | #Service | #Ingress

// Additionally apply our labels buildup to the resources
#Schema: {
	metadata: #metadata & #myLabels
}

#Deployment: {
	apiVersion: "apps/v1"
	kind: "Deployment"
	...
}

#Service: {
	apiVersion: "v1"
	kind: "Service"
	...
}

#Ingress: {
	apiVersion: "extensions/v1beta1"
	kind: "Ingress"
	...
}

Guards of Conditionality

Cue has an if statement called a guard which protects an area of code. This is not a conditional branch as much as a way to conditionally include or extend some Cue code. Recall that Cue is turing incomplete and thus doesn’t have concepts of loop or branches.

// use if guard to get even numbers in a list comprehension
nums: [1,2,3,4,5,6,7,8]
sqrs: [ for n in nums if mod(n,2) == 0 { n*n } ]

// use if guard to conditionally add fields
elems: [
	{ name: "a", public: true },
	{ name: "b", public: true },
	{ name: "c", public: false },
	{ name: "d" },
]

Elems: {
	hasPublicField: {
		for key, val in elems {
			// test against _|_ to see if a field exists
			if val.public != _|_ {
				"\(key)": val
			}
		}
	}
	stringifyPublic: {
		for key, val in elems {

			// there is no short-circuit, so we must nest guard instead
			if val.public != _|_ {

				// test conjunctions not equal to bottom for "truthiness"
				if (val.public & true) != _|_ {
					"\(key)": val & { PUB: "true" }
				}
				// note, there is no "else" clause
				if (val.public & false) != _|_ {
					"\(key)": val & { PUB: "false" }
				}
			}
		}
	}
}
cue eval guards.cue
nums: [1, 2, 3, 4, 5, 6, 7, 8]
sqrs: [4, 16, 36, 64]
elems: [{
    name:   "a"
    public: true
}, {
    name:   "b"
    public: true
}, {
    name:   "c"
    public: false
}, {
    name: "d"
}]
Elems: {
    hasPublicField: {
        "0": {
            name:   "a"
            public: true
        }
        "1": {
            name:   "b"
            public: true
        }
        "2": {
            name:   "c"
            public: false
        }
    }
    stringifyPublic: {
        "0": {
            name:   "a"
            PUB:    "true"
            public: true
        }
        "1": {
            name:   "b"
            PUB:    "true"
            public: true
        }
        "2": {
            name:   "c"
            PUB:    "false"
            public: false
        }
    }
}

How would you get all the elems which do not have a public field defined?

Last modified January 4, 2021: Small spelling fix (ded47a9)

2021 Hofstadter, Inc