# (chibi config)

This is a library for unified configuration management. Essentially it provides an abstract collection data type for looking up named values, two or more of which can be chained together. Values from more recent collections can be preferred as with an environment, or the values at multiple levels can be flattened together. Convenience routines are provided from loading these collections from files while allowing extensions such as configurations from command-line options.

## Background

As any application grows to sufficient complexity, it acquires options and behaviors that one may want to modify at startup or runtime. The traditional approach is a combination of command-line options, config files, environment variables, and/or other specialized settings. These all have various pros and cons:

name | pros | cons
--- | --- | ---
environment variables | implicit - no need to retype; can share between applications | unclear when set; unexpected differences between users; limited size
command-line options | explicit - visible each time a command is run; | verbose; limited size
config files | implicit; preserved - can be shared and version controlled | requires a parser

Environment variables are convenient for broad preferences, used by many different applications, and unlikely to change per user. Command-line options are best for settings that are likely to change between invocations of a program. Anything else is best stored in a config file. If there are settings that multiple users of a group or whole system are likely to want to share, then it makes sense to cascade multiple config files.

## Syntax

With any other language there is a question of config file syntax, and a few popular choices exist such as .ini syntax. With Scheme the obvious choice is sexps, generally as an alist. We use a single alist for the whole file, with symbols for keys and arbitrary sexps for values. The alists are intended primarily for editing by hand and need not be dotted, but the interface allows dotted values. Disambiguation is handled as with two separate functions, `(conf-get config key)` and `(conf-get-list config key)`, which both retrieve the value associated with `key` from `config`, in the latter case coercing to a list. The result is determined according to the structure of the alist cell as follows:

Cell | `conf-get` result | `conf-get-list` result
--- | --- | ---
`(key)` | `()` | `()`
`(key . non-list-value)` | `non-list-value` | `(non-list-value)`
`(key non-list-value)` | `non-list-value` | `(non-list-value)`
`(key (value1 value2 ...))` | `(value1 value2 ...)` | `(value1 value2 ...)`
`(key value1 value2 ...)` | `(value1 value2 ...)` | `(value1 value2 ...)`

Thus writing the non-dotted value will always do what you want. Specifically, the only thing to be careful of is if you want a single-element list value, even with `conf-get`, you should write `(key (value))`.

## Interface

Returns true iff `x` is a config object.

#### `(make-conf alist parent source timestamp)`

#### `(read-from-file file . opt)`

#### `(assoc-get alist key [equal? [default]])`

Utility analogous to `conf-get` on a pure alist. Returns the value of the cell in `alist` whose car is `equal?` to `key`, where the value is determined as the `cadr` if the cell is a proper list of two elements and the `cdr` otherwise. If no cell is found, returns `default`, or `#f` if unspecified.

#### `(assoc-get-list alist key [default])`

Equivalent to `assoc-get` but coerces its result to a list as described in the syntax section.

#### `(conf-head config)`

Returns just the base of `config` without any parent.

#### `(conf-load file [conf])`

Loads the config file `file`, prepending to `conf` if provided.

#### `(conf-load-in-path config-path file)`

Search for and load any files named `file` in the `config-path`, which should be a list of strings.

#### `(conf-load-cascaded config-path file [include-keyword])`

Similar to conf-load-in-path, but also recursively loads any "include" config files, indicated by a top-level `include-keyword` with either a string or symbol value. Includes are loaded relative to the current file, and cycles automatically ignored.

#### `(conf-get config key [default])`

Basic config lookup - retrieves the value from `config` associated with `key`. If not present, return `default`. In `conf-get` and related accessors `key` can be either a symbol, or a list of symbols. In the latter case, each symbol is used as a key in turn, with the value taken as an alist to further lookup values in.

#### `(conf-get-list config key [default])`

Equivalent to `conf-get` but coerces its result to a list as described in the syntax section.

#### `(conf-get-cdr config key . opt)`

Equivalent to `conf-get` but always returns the `cdr` as-is without possibly taking its `car`.

#### `(conf-get-multi config key)`

Equivalent to `conf-get-list` but returns a list of all cascaded configs appended together.

#### `(conf-extend config alist [source])`

Extends the config with anadditional alist.

#### `(conf-append a b)`

Joins two configs.

#### `(conf-unfold-key key value)`

Utility to create an alist cell representing the chained key `key` mapped to `value`.

#### `(conf-set config key value)`

Replace a new definition into the first config alist.

#### `(conf-specialize config key name)`

Lift specialized sections to the top-level of a config.

## Config Verification

#### `(conf-verify spec config [warn])`