The cue export command can be given any number of inputs to evaluate via file or package arguments. This page explains how the command interprets, assembles, and combines these inputs when handed different kinds and quantities of input.

No input arguments

When cue export is invoked with zero arguments its input is the single package in the current directory:

default-input.cue
package example

A: 1
B: 2.2 + A
C: 3.33 * B
TERMINAL
$ cue export
{
    "A": 1,
    "B": 3.2,
    "C": 10.656
}

The package is loaded as a package instance.

The command fails if more than one package exists in the current directory and the input isn’t explicitly specified:

package one

message: "this is package one"
package two

message: "this is package two"
TERMINAL
$ cue export
found packages "one" (1.cue) and "two" (2.cue) in "."

One input argument

The argument “.” refers to the single package in the current directory:

1.cue
package one

message: "this is package one"
TERMINAL
$ cue export .
{
    "message": "this is package one"
}

If you want to export the contents of a CUE package, but there is more than one package in the current directory, tell cue export which package to process by providing its import path as an argument:

package one

message: "this is package one"
package two

message: "this is package two"
TERMINAL
$ cue export .:two
{
    "message": "this is package two"
}

The previous example shows a rooted import path (.:two), as explained in cue help inputs.

If the only argument is the path to a data file then the file’s contents are interpreted in the encoding implied by its filename suffix, and the input processed by cue export is the resulting data:

path/to/some/data.yaml
message: this is a YAML data file
TERMINAL
$ cue export path/to/some/data.yaml
{
    "message": "this is a YAML data file"
}

Individual CUE files can also be referred to directly, by filename, instead of being used through their import path at the package level. However import paths, rather than CUE files, are the suggested way to refer to CUE as they automatically include new and renamed files in the package. Referring directly to CUE files requires you to keep cue export invocations up to date, manually, if you change the names of the files being used or add files to the package – potentially leading to errors due to missing configuration, if care is not taken to include every file that’s part of the package.

Input file encodings

The filename suffixes that cue export understands are described in cue help filetypes. These include .json, .yml, .yaml, .toml, and .cue. If a filename suffix is recognised, then cue export automatically interprets the file as the encoding implied by the suffix.

If the encoding of a file doesn’t match its filename suffix, use a qualifier to specify the encoding:

some-yaml.data
message: this YAML file has a .data suffix
TERMINAL
$ cue export yaml: some-yaml.data
{
    "message": "this YAML file has a .data suffix"
}

A qualifier dictates the encoding of every file that follows it, up to the next qualifier. In each separate cue export invocation (considering its parameters in order) after a qualifier has been used, all files are interpreted through that qualifier’s encoding until it’s overridden by a subsequent qualifier. There is no way for the invocation to re-engage the interpretation of filename suffixes. Qualifiers include json:, yaml:, and cue:, and are described in cue help filetypes.

Multiple input arguments

When you specify more than one argument then the input that cue export evaluates varies, based on what types of input you provide. Informally, you can think about all potential inputs being subdivided into these five types:

  • Complete CUE packages (a “CUE package”)
  • Individual CUE files that have a package clause near the top of the file (a “CUE package file”)
  • CUE files that do not have a package clause near the top (a “package-less CUE file”)
  • Non-CUE data files (a “data file”)
  • Non-CUE schema or constraint files, such as JSON Schema, OpenAPI, or Protobuf (a “constraint file”)

These types and names aren’t formally defined or specified - they’re just informal classifications that you can keep in mind as you read this guide.

CUE package inputs

A CUE package input can either be combined with other CUE package inputs, or it can be combined with any other input types. A CUE package input cannot be combined with other CUE package inputs and other input types at the same time.

Combining multiple CUE package inputs

When multiple CUE package inputs are specified then the resulting CUE evaluation is executed once for each package:

package one

A: 1
package two

B: 2
package three

C: 3
TERMINAL
$ cue export .:one :two .:three
{
    "A": 1
}
{
    "B": 2
}
{
    "C": 3
}

Multiple CUE package inputs cannot be combined with any other input types - only other CUE package inputs.

Combining one CUE package input with other input types

When a single CUE package input is specified alongside other input types then cue export unifies all the inputs and evaluates the result of this unification.

Any number of inputs belonging to other input types may be combined with a single CUE package input. The CUE package input must be the first input specified in the list of arguments.

Each input contributes the kind of information to the unification that you would expect: data file inputs contribute data; constraint file inputs contribute constraints; CUE package file and package-less CUE file inputs contribute data and constraints, depending on the CUE they contain.

Here’s an example demonstrating multiple input types being specified alongside a single CUE package:

package one

// Field x must be present.
x!: _
package min

x: >10
x: <=99
x: 50
TERMINAL
$ cue export .:one min.cue data.yml max.cue
{
    "x": 50
}

If CUE package file inputs are present then their package clauses need to be the same as each other, but don’t need to match the name of any CUE package input that’s present. By definition, package-less CUE file inputs don’t contain a package clause, so this requirement doesn’t affect them.

CUE package file inputs

As a reminder, CUE package file inputs are CUE files that contain a package clause near the top, and are distinguished from CUE package inputs as they’re referenced individually during an invocation, and not by a package import path. In other words, CUE package file inputs are any CUE files containing a package clause that you explicitly tell cue export to process.

When you mention a CUE package file input alongside a data file input, the inputs are unified, with any resulting concrete data being validated against any constraints that are present in the CUE:

package one

A:  1    // A is the concrete value 1.
B?: >100 // B must be greater than 100.
A: "some string"
B: 99
TERMINAL
$ cue export 1.cue data.yml
A: conflicting values "some string" and 1 (mismatched types string and int):
    ./1.cue:3:5
    ./data.yml:1:4
B: invalid value 99 (out of bound >100):
    ./1.cue:4:5
    ./data.yml:2:4

When you tell cue export to process a CUE package file input alongside a CUE package input, their package names don’t need to match. However, if you mention multiple CUE package file inputs, then the package names of all the CUE package file inputs must be the same as each other - but they still don’t need to match any CUE package input that’s also present. A package-less CUE file input (a CUE file that doesn’t contain a package clause) can be included alongside CUE package file and CUE package inputs.

In all these situations the inputs are unified, with any data being validated against any constraints that are present:

package one

// Field x must be present.
x!: _
package two

x: >10
package two

x: <=99
z: x * 2
x: 50
TERMINAL
$ cue export .:one min.cue max.cue data.yml calc.cue
{
    "z": 100,
    "x": 50
}

Package-less CUE file inputs

cue export handles package-less CUE file inputs identically to data file inputs - they’re treated equivalently, and are interchangeable as cue export arguments. However, where data file inputs can only introduce concrete data, package-less CUE file inputs can also include constraints, calculated fields, and all other CUE language features alongside their concrete data. Their contributions to the evaluation are unified as you might expect - their constraints add to the set of constraints that validate the concrete data, and their concrete data is validated against the set of constraints derived from all constraint-related input types.

Data file inputs

Data file inputs contain concrete data, encoded as JSON, YAML or TOML. If they are specified alongside other input types then they are treated as described in the sections above. If they are processed without other input types being present, then cue export simply unifies the data they contain. As always, unification is recursive, processing nested data structures to an arbitrary depth:

{
    "A": 1,
    "B": {
        "C": 2
    },
    "E": [
        4,
        {
            "F": 5
        },
        7
    ]
}
A: 1
B:
  D: 3
E:
  - 4
  - G: 6
  - 7
TERMINAL
$ cue export data.yml data.json
{
    "A": 1,
    "B": {
        "C": 2,
        "D": 3
    },
    "E": [
        4,
        {
            "F": 5,
            "G": 6
        },
        7
    ]
}

If any values don’t unify successfully, at any depth, an error message is printed:

A:
  - 1
  - B: 2
  - 4
{
    "A": [
        1,
        {
            "B": 3
        }
    ]
}
TERMINAL
$ cue export data.yml data.json
A: incompatible list lengths (2 and 3)
A.1.B: conflicting values 2 and 3:
    ./data.json:5:18
    ./data.yml:3:8

Constraint file inputs

A constraint file input contains one or more data constraints expressed in a non-CUE encoding such as JSON Schema, OpenAPI or Protocol Buffers. They can be combined with all other input types, with their constraints being unified with constraints from any other inputs, which are then used to validate any data inputs.

A constraint file input can be used without any CUE-based inputs. cue export can validate data directly against a constraint file input:

{
    "$schema": "http://json-schema.org/draft-07/schema#",
    "type": "object",
    "properties": {
        "A": {
            "type": "integer",
            "minimum": 100
        },
        "B": {
            "type": "number",
            "maximum": 99
        }
    }
}
A: 50
B: 1000
TERMINAL
$ cue export data.yml schema.json
A: invalid value 50 (out of bound >=100):
    ./schema.json:7:13
    ./data.yml:1:4
B: invalid value 1000 (out of bound <=99):
    ./schema.json:11:13
    ./data.yml:2:4

cue export recognizes JSON Schema and OpenAPI constraint file inputs through their signature fields. This behaviour can be changed by using a qualifier to instruct the command to treat the constraint file input as pure data:

schema.json
{
    "$schema": "http://json-schema.org/draft-07/schema#",
    "type": "object",
    "properties": {
        "A": {
            "type": "integer",
            "minimum": 100
        },
        "B": {
            "type": "number",
            "maximum": 99
        }
    }
}
TERMINAL
$ cue export json: schema.json
{
    "$schema": "http://json-schema.org/draft-07/schema#",
    "type": "object",
    "properties": {
        "A": {
            "type": "integer",
            "minimum": 100
        },
        "B": {
            "type": "number",
            "maximum": 99
        }
    }
}

Standard input stream

The standard input stream of the cue export command is available for processing as any file-based input type, through its pseudo-filename of “-”. By default, standard input is processed as CUE – either as a CUE package file or package-less CUE file input, depending if the stream contains a package clause or not.

To indicate that cue export should interpret the stream through a different encoding, use a qualifier:

data.yml
A:
  - b
  - c
TERMINAL
$ cat data.yml | cue export yaml: -
{
    "A": [
        "b",
        "c"
    ]
}

Non-CUE data location

During evaluation, cue export unifies all its inputs and, by default, places the contents of any data file inputs at the top-level of the evaluation space. You can change this behaviour by specifying a static or dynamic location for all data file inputs using the --path (-l) flag. This flag is described in cue help flags and is demonstrated below.

Static locations

To specify a static location for the data file inputs during evaluation, provide the --path (-l) flag with a CUE expression that ends with a colon (:):

data.yml
data: true
TERMINAL
$ cue export data.yml --path foo:
{
    "foo": {
        "data": true
    }
}

Increase the depth and nesting of the location by adding more components to the expression:

data.yml
data: true
TERMINAL
$ cue export data.yml -l foo:bar:baz:
{
    "foo": {
        "bar": {
            "baz": {
                "data": true
            }
        }
    }
}

You can also provide multiple path components through repeated instances of the flag. For example, the previous example is equivalent to -l foo: -l bar: -l baz:.

The combined values of all --path (-l) flags that are present in a cue export invocation apply to all the data file inputs. The flags' positions in the invocation are irrelevant, no matter where they sit relative to any arguments defining the inputs to be processed. When specifying a static location, this means that the data in all data file inputs is unified at the location provided. If there are multiple data file inputs, all their contents must unify without errors:

data.json
{
    "data": false
}
data.yml
data: true
TERMINAL
$ cue export -l foo: data.yml -l bar: data.json -l baz:
foo.bar.baz.data: conflicting values true and false:
    ./data.json:2:13
    ./data.yml:1:7

Dynamic locations

As you saw in the previous section, a trailing colon (:) tells cue export that a value in the location expression should be interpreted statically. If the colon is omitted, then the value is resolved dynamically - in the context of the data that’s present:

data.yml
foo: "a"
data: true
TERMINAL
$ cue export data.yml -l foo
{
    "a": {
        "foo": "a",
        "data": true
    }
}

Functions from the CUE standard library may be included in the specification of a dynamic expression, without their containing packages being imported:

data.yml
foo: "a"
data: true
TERMINAL
$ cue export data.yml -l 'strings.ToUpper(foo)'
{
    "A": {
        "foo": "a",
        "data": true
    }
}

Adding the --with-context flag changes the context in which the dynamic expression is evaluated, allowing it to refer to information about a data file input (such as the file’s name) as described in cue help flags:

data.yml
data: true
TERMINAL
$ cue export data.yml -l 'path.Base(filename)' --with-context
{
    "data.yml": {
        "data": true
    }
}

Data files with multiple documents

Some encodings, such as YAML, permit multiple documents to be contained in a single file. When processing such data file inputs, cue export offers a choice of how to combine these documents.

The default behaviour is to merge the documents normally, as is done via unification. Because they are merged, their contents must be values that unify successfully.

data.yml
---
A: 1
---
B: 2
---
C: 3
TERMINAL
$ cue export data.yml --merge
{
    "A": 1,
    "B": 2,
    "C": 3
}

Merging is the command’s default behaviour, so the --merge flag is optional.

Alternatively, you can opt for multiple documents to be placed in a list by providing the --list flag. This behaviour permits the contents of each document to be addressed individually, rather than as the unified value that would be present when using the --merge flag. This can be useful when each document needs to be processed separately, or when documents contain values which cannot unify:

data.yml
---
A: 1
---
A: 2
---
A: 3
TERMINAL
$ cue export data.yml --list
[
    {
        "A": 1
    },
    {
        "A": 2
    },
    {
        "A": 3
    }
]

Information injection

System information and arbitrary values can be made available to an evaluation by using tag variables and tag attributes. They are described in cue help injection, and are demonstrated in the how-to guides Injecting system information into an evaluation using a tag variable and Injecting a value into an evaluation using a tag attribute.