Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Enums, Structs, and Unions

These definitions let schemas model more than flat tables.

Enums

[[enums]]
name = "Rarity"
values = ["Common", "Uncommon", "Rare", "Epic", "Legendary"]

Enums keep source data readable while generated code receives a constrained type.

Aliases can keep imported or legacy names readable:

[[enums.aliases]]
name = "Purple"
alias = "Epic"

Structs

[[structs]]
name = "ResourceCost"

[[structs.fields]]
name = "kind"
type = "enum<ResourceKind>"

[[structs.fields]]
name = "id"
type = "i32"

[[structs.fields]]
name = "count"
type = "i32"
range = [1, 999999]

Use structs for nested values that appear in many places. A field can reference a struct with type = "struct<ResourceCost>".

Struct fields use the same field properties as table fields, including name, type, default, comment, range, length, parser, and scope. Table-specific properties such as key and from are not meaningful for normal struct fields. See Types for the full field reference.

In cell-based inputs, a struct field can be written as JSON object text by default:

{"kind":"Gold","id":0,"count":100}

For compact cells, declare parser = { kind = "tuple" } on the field that references the struct. Tuple values follow the struct field order:

Gold,0,100

Unions

Use a union when one field can contain different shapes. For example, an event condition might be either “quest completed” or “player has item”:

{"type":"QuestCompleted","quest_id":5002}
{"type":"HasItem","item_id":1001,"count":2}

The type value selects which variant is present. The rest of the fields depend on that variant.

[[unions]]
name = "RewardAction"
tag = "type"

[[unions.variants]]
name = "AddItem"

[[unions.variants.fields]]
name = "item_id"
type = "ref<Item.id>"

[[unions.variants.fields]]
name = "count"
type = "i32"

[[unions.variants]]
name = "UnlockStage"

[[unions.variants.fields]]
name = "stage_id"
type = "ref<Stage.id>"

Use unions when a field can contain one of several tagged shapes. Examples include conditions, rewards, triggers, and scripted actions.

The union tag defaults to type if omitted. Source data must include that tag with the variant name. The remaining fields must match the selected variant; unknown fields and missing non-optional variant fields are validation errors.

The most direct Excel or CSV form is JSON object text in one cell:

Field typeCell value
union<RewardAction>{"type":"AddItem","item_id":1001,"count":2}

For a list of union values, declare parser = { kind = "json" } and write a JSON array:

[[tables.fields]]
name = "actions"
type = "list<union<RewardAction>>"
parser = { kind = "json" }
[
  {"type":"AddItem","item_id":1001,"count":2},
  {"type":"UnlockStage","stage_id":9002}
]

If you do not want JSON in Excel or CSV cells, a single union<T> field can be expanded into several columns. This action field is one union value:

[[tables.fields]]
name = "action"
type = "union<RewardAction>"
parser = { kind = "tagged_columns" }

The Excel sheet then has columns like this:

ABCDEF
idnameaction.typeaction.item_idaction.countaction.stage_id
1Give SwordAddItem10012
2Open StageUnlockStage9002

action.type contains the variant name. An AddItem row fills only item_id and count; an UnlockStage row fills only stage_id. Columns for other variants stay empty.

tagged_columns is only valid on a field whose type is exactly union<T>; it cannot be applied directly to list<union<T>>. When a parent field needs several union values, put each union value in a child row and derive the parent list from that child table:

[[tables.fields]]
name = "actions"
type = "list<union<RewardAction>>"
from = { table = "EventActionEntry", parent_key = "id", child_key = "event_id", field = "value", order_by = "seq" }

[[tables]]
name = "EventActionEntry"
mode = "list"

[[tables.fields]]
name = "event_id"
type = "ref<EventRule.id>"

[[tables.fields]]
name = "seq"
type = "i32"

[[tables.fields]]
name = "value"
type = "union<RewardAction>"
parser = { kind = "tagged_columns", prefix = "" }

The parent EventRule sheet keeps ordinary columns:

AB
idname
1First Event

The child EventActionEntry sheet stores one action per row:

ABCDEF
event_idseqtypeitem_idcountstage_id
11AddItem10012
12UnlockStage9002

On export, EventRule.actions receives two union values ordered by seq. The prefix = "" option makes the child table columns use plain names such as type, item_id, count, and stage_id; do not use an empty prefix if those names conflict with other fields on the same table.

See Cell Parsers for the exact column rules.

In TOML data files, unions can be written as normal nested tables:

[[rows]]
id = 1
condition = { type = "QuestCompleted", quest_id = 5002 }
actions = [
  { type = "AddItem", item_id = 1001, count = 2 },
  { type = "UnlockStage", stage_id = 9002 },
]