Reading and writing TOML

The cue command natively supports reading and writing TOML files and data. TOML can be processed by CUE’s wide range of data, schema, and policy validation capabilities. Data in any supported encoding can be read and exported as TOML – as demonstrated here by cue export unifying its TOML, JSON, and CUE input files and producing TOML:

a = "1"

[b]
c = 2.2

[b.d]
e = 3
{
    "f": "4",
    "g": 5.5
}
b: _
g: _

h: "six"
b: d: i: g + b.d.e
TERMINAL
$ cue export --out toml a.toml b.json c.cue
a = '1'
f = '4'
g = 5.5
h = 'six'

[b]
c = 2.2

[b.d]
e = 3
i = 8.5

The cue command can read and write a range of other formats as well as TOML.

Validating TOML files against a schema

CUE is often used to make systems safer without having to teach the underlying system components about CUE. Because the cue tool can validate TOML files using CUE’s powerful and compact constraint syntax, it’s easy to add “pre-flight” checks to existing processes with CUE.

In this example, cue vet is used to check that a hypothetical system’s TOML input files are valid - and catches a problematic deployment early in the process:

schema.cue
import "strings"

#Config: {
	cluster!:    strings.MaxRunes(16)
	region!:     #Region
	repository!: =~#"^source\.company\.example/"#
	tags?: [...#Tags]
}
#Region: "APAC" | "IMEA"
#Tags:   "prod" | "stage" | "qa" | "test" | "dev"
cluster = 'live05'
region = 'IMEA'
repository = 'source.company.example/alpha'
tags = ['prod']
cluster = 'live03333333333333'
region = 'UK'
repository = 'github.com/Alex_Personal_Account/alpha-fork'
tags = ['dev']
cluster = 'live05'
region = 'APAC'
repository = 'source.company.example/alpha'
TERMINAL
$ cue vet -c schema.cue -d '#Config' config-a.toml config-b.toml config-c.toml
region: 2 errors in empty disjunction:
region: conflicting values "APAC" and "UK":
    ./config-b.toml:2:10
    ./schema.cue:9:10
region: conflicting values "IMEA" and "UK":
    ./config-b.toml:2:10
    ./schema.cue:9:19
cluster: invalid value "live03333333333333" (does not satisfy strings.MaxRunes(16)):
    ./schema.cue:4:15
    ./config-b.toml:1:11
    ./schema.cue:4:32
repository: invalid value "github.com/Alex_Personal_Account/alpha-fork" (out of bound =~"^source\\.company\\.example/"):
    ./schema.cue:6:15
    ./config-b.toml:3:14

Processing and transforming TOML files

The cue tool can read and transform TOML files, producing output data in any shape that’s required. For example:

transform.cue
a: int
b: int
c: 1 + a*b
data.toml
a = 5
b = 4
TERMINAL
$ cue export data.toml transform.cue --out toml
a = 5
b = 4
c = 21

Learn more about transforming data with CUE in these How-to guides:

These guides explain things in terms of JSON data, but the techniques they demonstrate apply equally to TOML because CUE treats all data encodings as equivalent.

Embedding TOML file data inside CUE

Available from CUE v0.12.0

The file embedding feature allows data files (including TOML) to be read when some CUE is evaluated. This provides an alternative way to use CUE to validate data files against schemas and constraints, and also gives CUE configurations access to data stored in non-CUE files:

example.cue
@extern(embed)

package p

_conf: _ @embed(file=config.toml)
_data: _ @embed(glob=env/*.toml)

info: {
	version: _conf.version
	source:  _data["env/\(_conf.source).toml"].text
}
source = 'bar'
version = '1.42.0'
text = 'Some foo'
text = 'A bar'
TERMINAL
$ cue export --out toml
[info]
source = 'A bar'
version = '1.42.0'

File embedding is available from CUE v0.12.0 onwards. Find out more about this powerful validation feature in Embedding files in a CUE evaluation.

Encoding TOML inside CUE

CUE is frequently used to generate configuration files. Some systems allow their configuration files to contain TOML encoded in string fields, irrespective of the file’s main data format.

CUE’s standard library provides a built-in toml package containing functions that generate and parse TOML from within CUE, as shown here.

Generating encoded TOML

In this example a Kubernetes ConfigMap contains a TOML file encoded as a single string field, in a YAML document. This is enabled by the toml.Marshal function:

config.cue
import "encoding/toml"

configMap: data: "point.toml": toml.Marshal({
	x: 1.2
	y: 3.45
})
TERMINAL
$ cue export config.cue --out yaml
configMap:
  data:
    point.toml: |
      x = 1.2
      y = 3.45

Parsing encoded TOML

The toml.Unmarshal function performs the reverse operation to toml.Marshal: it turns a string containing TOML into the structure represented by the encoded data.

Here, some encoded TOML data (a Rust crate manifest) is emitted as YAML:

config.cue
import "encoding/toml"

_cargo: """
	[package]
	name = "hello_world"
	version = "0.1.0"
	edition = "2021"

	[dependencies]
	serde = "1.0"
	"""
output: cargo: toml.Unmarshal(_cargo)
TERMINAL
$ cue export config.cue --out yaml
output:
  cargo:
    package:
      name: hello_world
      version: 0.1.0
      edition: "2021"
    dependencies:
      serde: "1.0"

Converting TOML files to CUE

The cue import command can create a CUE file for each TOML file it’s given – and can even recognise encoded YAML and JSON fields, and convert those structures recursively.

Examples of this command being used can be found in the cue import reference documentation.