# Confiner - A tree-like config language.
```
/\
< >
\/
CONF
CONF
CONF||CONF
CONF||CONF
CONF||||CONF
CONF CONF CONF
CONF CONF CONF
CONF CONF CONF CONF
CONF |||| CONF
||||
```
(Config + Conifer, get it?)
## For example
A simple webserver config...
```
my_server: server
{
addr = "127.0.0.1:80",
my_uri_handler: uri
{
mount = "/files",
pattern = [ "" ],
file_service: raw_files
{
path = ./files
}
}
}
```
## Features
### Blocks
Confiner files contain a set of blocks, each of which has a set of key-value
properties, and child blocks. This is similar to HTML or device-tree files.
Blocks have a kind, and an optional globally unique name. The syntax for a block
begins with an optional name identifier, followed by a colon and the block kind,
then a pair of braces containing comma-separated properties and children. e.g.
```
block_name: block_kind
{
property="expr",
: child_1_kind
{
property="expr",
subchild: subchild_kind {}
},
child_2: child_2_kind {}
}
```
### Expression types
Basic data types can be expressed in a sensible way:
* Integers (`1`, `-2`, `0x03`, ...)
* Floats (`1.0`, `1e-3`, ...)
* Strings (`"hello world"`, `"Line 1\nLine 2"`, `"\"quoted\""`, ...)
* Lists (`[1, 2, 3]`, ...)
* Maps *with string keys* (`{ key = "value" }`, ...)
There are a couple of more unique types of expressions.
#### Enums
These help to unambiguously serialize Rust's enum types. They consist of an enum
identifier prefixed with an exclamation mark, followed optionally by data for
that enum variant.
For example, consider the following enum:
```
enum MyEnum
{
Unit,
NewType(String),
Tuple(i32, i32),
Struct { field: bool }
}
```
This could be serialized as follows:
* `!Unit`
* `!NewType "value"`
* `!Tuple [i32, i32]`
* `!Struct { field = true }`
#### Paths
Paths are expressions that expand to the absolute path of a file, specified
relative to the location of the confiner file. These expressions respect
`@include` directives, and always evaluate to the same bytes, regardless of
whether the file was included or deserialized directly.
They are written as a relative path, prefixed with `.`. They are not quoted,
and cannot contain unescaped spaces. For example:
* `.` (The directory containing the confiner file)
* `./file.txt` (A file in the same directory as the confiner file)
* `./directory\ with\ spaces/file.txt`
### References
At the top-level of a confiner file, references can be defined. Rather than
diretly creating a block, these blocks can be placed elsewhere by "using" the
reference. The same block can be placed in multiple places, allowing for more
complex structures than simple trees.
The syntax for defining a reference is the same as for defining a normal
top-level block, but with a prefixed "&". Reference blocks must be named.
The syntax for using a reference is simply an "&" followed by the name of the
reference.
```
&my_reference: kind { }
normal_block: kind
{
&my_reference,
child_block: kind
{
&my_reference
}
}
```
### Included files
Files can be included using the `@include <path>` syntax. Each included file is
evaluated at most once, and any references or top-level blocks from the included
file are treated equivalently to those in the file containing the
`@include <path>`.
The `@include <path>` syntax uses for same relative paths logic as the path
expression.
```
@include ./other_file.conf
@include ./subdir/file.conf
```