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:
package example
A: 1
B: 2.2 + A
C: 3.33 * B
$ 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"
$ 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:
package one
message: "this is package one"
$ 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"
$ 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:
message: this is a YAML data file
$ 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:
message: this YAML file has a .data suffix
$ 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
$ 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
$ 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
$ 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
$ 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
$ 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
}
]
}
$ 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
$ 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": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"A": {
"type": "integer",
"minimum": 100
},
"B": {
"type": "number",
"maximum": 99
}
}
}
$ 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:
A:
- b
- c
$ 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: true
$ 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: true
$ 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": false
}
data: true
$ 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:
foo: "a"
data: true
$ 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:
foo: "a"
data: true
$ 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: true
$ 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.
---
A: 1
---
B: 2
---
C: 3
$ 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:
---
A: 1
---
A: 2
---
A: 3
$ 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.
Related content
- Reference: The cue export command
- Language Tour: Unification
- Reference: cue help filetypes
- Reference: cue help flags
- Reference: cue help injection
- Reference: cue help inputs
- Concept Guide: Modules, Packages, and Instances
- Language Tour: Standard Library
- How-to Guide: Injecting system information into an evaluation using a tag variable
- How-to Guide: Injecting a value into an evaluation using a tag attribute