YAML

How CUE integrates with the YAML data-serialization standard.

Intro

Unlike with JSON, CUE is not a superset of YAML. One of the design goals of CUE was to be easily machine generatable and modifiable. The sensitivity to indentation and the lexical obscurity of the typing of tokens make YAML too bug prone for this purpose.

Consider this piece of yaml:

phrases:
  quote1:
    lang: en
    text: Never regret anything that made you smile.
    attribution: Mark Twain

  proverb:
    lang: no
    text: Stemmen som sier at du ikke klarer det, lyver.

See the problem? The language for the Norwegian proverb is set to no, which is interpreted as false in YAML.

Luckily, CUE can help catch those pesky YAML quirks quickly and swiftly.

Command line tool

Validate YAML files

The vet command of the cue command line tool can validate YAML files using a CUE schema.

Consider these two files:

# ranges.yaml
min: 5
max: 10
---
min: 10
max: 5
// check.cue
min?: *0 | number    // 0 if undefined
max?: number & >min  // must be strictly greater than min if defined.

The cue vet command can verify that the values in ranges.yaml are correct by just mentioning the two files on the command line.

$ cue vet ranges.yaml check.cue
max: invalid value 5 (out of bound >10):
    ./check.cue:2:16
    ./ranges.yaml:5:7

Import YAML

The import command of the cue command line tool can convert YAML files into CUE. It can even embedded structured YAML and JSON and convert those recursively.

YAML in CUE

The encoding/yaml builtin package provides various builtins to parse, generate, or validate YAML from within CUE.

Validate

If the YAML string from our introduction were embedded within a CUE file, the fix would be the following.

// dim.cue
import "encoding/yaml"

// Phrases defines a schema for a valid phrase.
#Phrases: {
    phrases: [string]: #Phrase

    #Phrase: {
        lang:         #LanguageTag
        text:         !=""
        attribution?: !="" // must be non-empty when specified

    }
    #LanguageTag: =~"^[a-zA-Z0-9-_]{2,}$"
}

// phrases is a YAML string with a field phrases that is a map of Phrase
// objects.
phrases: yaml.Validate(#Phrases)

phrases: """
    phrases:
      # A quote from Mark Twain.
      quote1:
        lang: en
        text: Never regret anything that made you smile.
        attribution: Mark Twain

      # A Norwegian proverb.
      proverb:
        lang: no
        text: Stemmen som sier at du ikke klarer det, lyver.
    """

By defining a schema (called definition in CUE) for the allowed values of a YAML string, we were able to catch the error before it made it into production.

$ cue vet dim.cue
phrases: error in call to encoding/yaml.Validate: conflicting values false and
LanguageTag (mismatched types bool and string):
    ./dim.cue:18:10

Create

The builtin encoding/yaml.Marshal generates YAML from within CUE.

import "encoding/yaml"

configMap: data: "point.yaml": yaml.Marshal({ x: 4.5 y: 2.34 })

{
    "configMap": {
        "data": {
            "point.yaml": "x: 4.5\n\"y\": 2.34\n"
        }
    }
}

Parse

The inverse is also possible

import "encoding/yaml"

data: """ x: 4.5 y: 2.34 """ point: yaml.Unmarshal(data)

{
    "data": "x: 4.5\ny: 2.34",
    "point": {
        "x": 4.5,
        "y": 2.34
    }
}