Attributes



Attributes are a form of markup in CUE intended for tools building on Cuelang. The cue tool has several reserved attributes and you can make your own attributes with their own meanings.

Overview

Attributes are added to fields and structs as a form of markup. They are not processed during evaluation, only tracked like comments. Nor are they accessible from within CUE and require you to write Go to work with them.

Despite being simple markup, attributes offer significant flexibility and configurability for custom Go tools. They were originally added to help map between different representations like Go and Protobuf. However, you can assign any meaning or behavior to them as they are only interpretable by your applications. In that sense they are a lot like Go struct tags, for the familiar.

Attributes have the following syntax:

  • they start with @, have a label, and parentheses
  • appear after fields or in structs
  • can have comma separated keys with optional values
  • values are strings and can have any format you wish

format.cue

	// field attribute
foo: "bar" @attr()

// declaration attribute
foo: {
	@attr()
	bar: string
}

// they can have keys
any: _ @attr(foo,bar)

// keys have optional values
any: _ @attr(key1,key2=value,key3="foo;bar")

cue’s attributes

The cue tool has several predefined attributes. You can run cue help injection at the command line for quick reference.

@tag() is used for injecting data from the command line

You can use this capability to inject data and control the values which get evaluated.

inject.cue

package main

env:  string | *"dev" @tag(env)
host: "\(env).domain.com"

cue export inject.cue

env:  "dev"
host: "dev.domain.com"

cue export inject.cue -t env=prod

env:  "prod"
host: "prod.domain.com"

Use the type option to set interpretation and short for commonly used values.

cue eval -t val=3.14 inject-type.cue

val: number @tag(val,type=int)

cue eval -t prod inject-short.cue

env: string @tag(env,short=prod|staging)
@if() is used for including a file when a tag is present

Use this special built tag to include entire files in evaluation.

if-main.cue

package main

import "path"

env:  string
host: string

// urlPath is agnostic of environment
urlPath: "/path/to/asset"

// url is derived from host and urlPath
url: "https://" + path.Join([host, urlPath])

if-qa.cue

@if(qa)
package main

env:  "qa"
host: "qa.prod.com"

if-prod.cue

@if(prod)
package main

env:  "prod"
host: "prod.mod.com"

cue eval .

import "path"

env:     string
host:    string
urlPath: "/path/to/asset"
url:     "https://" + path.Join([host, urlPath])

cue eval -t qa .

env:     "qa"
host:    "qa.prod.com"
urlPath: "/path/to/asset"
url:     "https://qa.prod.com/path/to/asset"

cue eval -t prod .

env:     "prod"
host:    "prod.mod.com"
urlPath: "/path/to/asset"
url:     "https://prod.mod.com/path/to/asset"
@go() connect representations

CUE adds struct tags during Go code import. Currently the following will be carried over ([go,yaml,json,toml,xml,protobuf])

@embed() has been proposed for embedding other files into CUE

This would work similar to Go’s embedding system.

Propagation

There are rules for how attributes propagate through unification, structural embedding, and imports. This allows you to add attributes to a definition and they will show up in the values. You can also add to the attributes.

propagations-fields.cue

	// Attributes are collected
x: _ @foo(bar) @cow(moo)
x: _ @foo(baz)

// this can be problematic
n: int @protobuf(1,int64)
n: int @protobuf(2,int64)

// field attributes don't propegate
y: x

cue eval -A propagation-fields.cue

x: _   @foo(bar) @cow(moo) @foo(baz)
n: int @protobuf(1,int64) @protobuf(2,int64)
y: _

propagations-struct.cue

#X: {
	@model(db)
		uuid: string @key(primary)
} @app(backend) // does not propagate

x: #X & {
	@model(ui) // can build up

	uuid: string @key(secondary) // can conflict
}

y: {
	#X
	uuid: "abc-123"
}

cue eval -A propagation-struct.cue

#X: {
	@model(db)
	uuid: string @key(primary)
} @app(backend)
x: {
	@model(db)

	@model(ui) // can build up
	uuid: string @key(primary) @key(secondary)
}

Field attributes do not propagate due to the following issue:

field-propagation-issue.cue

#A: {
	x: int @protobuf(1,int64)
	y: int @protobuf(2,int64)
}

a: #A
a: {
	x: 1
	y: x // propagation here
}

cue eval -A field-propagation-issue.cue

#A: {
	x: int @protobuf(1,int64)
	y: int @protobuf(2,int64)
}
a: {
	x: 1 @protobuf(1,int64)
	y: 1 @protobuf(2,int64)
}

// would result in a conflict for indexes
a: {
	y: 1 @protobuf(1,int64) @protobuf(2,int64)
}

Working with attributes

To work with attributes you have to write Go code. cue does this for the predefined attributes. See the go-api/attributes section for details and code snippets.

We'll never share your email with anyone else.
2022 Hofstadter, Inc