User Guide

Yaml Cheat Sheet

Usually YAML structure is denoted by indentation.

Here is an example of Yaml mapping (or dictinary, or associated array), used to fill in some application’s structure:

database-connection:
    host: localhost
    port: 1878
    encoding: utf-8

The top-level thing is almost always a mapping.

Sequences (or arrays, or lists) are denoted using - at the start of each line, for example:

urls:
- http://rust-lang.org
- http://rust-quire.readthedocs.io

Note: the space is required after both, the colon : used to denote a mapping and a dash - used to denote a sequence. This makes it possible to differentiate between urls written in the last example and a mapping example before. If you’re unsure use quotes:

urls:
- "http://rust-lang.org"
- 'http://rust-quire.readthedocs.io'

Values may have arbitrarily complex structure:

databases:
  slaves:
  - host: slave1.example.com
    port: 1245
  - host: slave2.example.com
    port: 1245

There is a short form of mappings and sequences, it’s markup includes curly braces {} and square brackets [] respectively. When this form is used, indentation is no more significant, until last brace or bracket is closed. Example:

databases:
  slaves: [{host: slave1, port: 1245},
    {host: slave2, port: 1245}]
languages [ru, uk, us]

Quire Tricks

Underscore Names

In most cases names mapping keys starting with underscore are ignored. For example:

host: localhost
_port: 1778  # this is ignored
port: 2021

In this case _port field is ignored by a validator. It can be used to effectively comment out the piece of config (of arbitrary depth). But data might must be valid Yaml anyway, and may include ancors.

Often, this trick is used to anchor some things in a more comfortable way, for example this config:

listen:
- host: 127.0.0.1
  port: &port 1279
- host: 172.0.0.1
  port: *port

Could be rewritten as:

_port: &port 1278

listen:
- host: 127.0.0.1
  port: *port
- host: 172.0.0.1
  port: *port

Note two things:

  • The latter example is more “symmetric” in some sense, you can reorder rows without rewriting anchors.
  • Also it’s convenient to move commonly changed parts to the top of the configuration file if config is big

Integers

Integers can be of base 10, just like everybody used to. It can also start with:

  • 0x to be interpreted as base 16
  • 0o meaning octal representation
  • 0b for bitmasks

Numbers can also be split into group to be easier to read using underscores: 1_024, 16_777_216.

Units

A lot of integer values in configuration files are quite big, e.g. should be expressed in megabytes or gigabytes. Instead of common case of making default units of megabytes or any other arbitrary choice, quire allows to specify order of magnitude units for every integer and floating point value. E.g:

int1: 1M
int2: 2k
int3: 2 ki

Results into the following, after parsing:

int1: 1000000
int2: 2000
int3: 2048

Note that there is a difference between prefixes for powers of 1024 and powers of the 1000.

The following table summarizes all units supported:

Unit Value
k 1000
ki 1024
M 1000000
Mi 1048576
G 1000000000
Gi 1073741824

Includes

Currently rust-quire supports a single kind of include. Includes are expanded after parsing the yaml but config validation. It has the following consequences:

  • Both include origin and included files are valid Yaml files on it’s own.
  • The included data is contained at the place where directive is (unlike many other configuration systems where inclusion usually occurs at the top level of the config), but you can include at the top level of the config too
  • All anchors are local to the file, you can’t reuse anchors from an included file.
  • Include directives can be arbirarily nested (up to the memory limit)

It depends on how API is used, but usually file name of the include directive is expanded relative to a file that contains include (in fact relative to the name under which file is opened in case it symlinked into multiple places)

Include Another Yaml

The !*Include tag includes the contents of the file replaceing the node that contains tag. For example:

# config.yaml
items: !*Include items.yaml
# items.yaml
- apple
- cherry
- banana

Is equivalent of:

items:
- apple
- cherry
- banana

Include Sequences of Files

The !*IncludeSeq tag includes files matched by glob as a sequence:

items: !*IncludeSeq "fruits/*.yaml"

Can be parsed as:

items:
- apple
- banana
- cherry

This depends on the exact application, but usually files returned by glob are sorted in alphabetical order. All the power of globs is supported, so you can do:

items: !*IncludeSeq "files/**/*.yaml"

Another trick is merge multiple files, each with it’s own set of keys into a single one (see map-merge below):

# file1.yaml
key1: 1
key2: 2
# file2.yaml
key3: 3
key4: 4
# main.yaml
<<: !*IncludeSeq "configs/*.yaml"

This results into the following config:

key1: 1
key2: 2
key3: 3
key4: 4

Note: merging is not recursive, i.e. top level keys are considered as a whole, even if they are dicts.

Include Mapping from Files

The !*IncludeMap tag works similarly to !*IncludeSeq but requires to mark part of a name used as a key. For example:

items: !*IncludeMap "fruits/(*).yaml"

Might result into the folowing:

items:
  apple: { color: orange }
  banana: { color: yellow }
  cherry: { color: red }

You can parenthesize any part as well as a whole path:

files: !*IncludeMap "(**/*.yaml)"

Merging Mappings

We use standard YAML way for merging mappings. It’s achieved using << key and either mapping or a list of mappings for the value.

The most useful merging is with aliases. Example:

fruits: &fruits
  apple: yes
  banana: yes
food:
  bread: yes
  milk: yes
  <<: *fruits

Will be parsed as:

fruits:
  apple: yes
  banana: yes
food:
  bread: yes
  milk: yes
  apple: yes
  banana: yes

Unpacking Sequences

Similarly to map merging we have a method to join two sequences, for example:

_wild: &wild_animals
- tiger
- lion
_pets: &domestic_animals
- cat
- dog

animals:
- !*Unpack [*wild_animals, *domestic_animals]

The key thing in the example is !*Unpack tag.

Note, you always need to have a two nested lists in, i.e. this is valid: !*Unpack [[value]], but this !*Unpack [value] isn’t. This is required in order to make tags in encompassed value work.