Module Kwconfig

module Kwconfig: sig .. end

Refer Based Config Files

This module implements simple config files in refer format (see Kwrefer).

A config file is parsed by Kwconfig.parse, which makes certain assumptions about the refer file. Before use, the config file can be validated for the presence of required fields, and the values of provided fields can themselves be validated, "fixed up" or modified (Kwconfig.fixup), and missing optional fields can be populated with default values (Kwconfig.defaults). The values of a field are extracted from the parsed Kwconfig.t with Kwconfig.find, or en masse with Kwconfig.section.

Config File Assumptions

A config file consists of distinct sections, which are the subsets of the refer records determined by the values of a distinguished field. For example, you might give your records a "section" field (the field name is up to you) and use:

%section ui

for records concerned with the user interface, and:

%section db

for records concerned with the database.

When you parse the config file, you would specify the distinguished field like so: parse ~section:"section"; any records that don't have %section field are lumped together in what is effectively their own anonymous section.

Very simple config files with only one section don't need a distinguished section field; just call parse withouth the ~section parameter and don't specify the ~section parameter for any other calls. Anytime you don't provide a ~section, you are referring to the anonymous section.

Technically, the parsed config file or section M.t is keyed on string options; explicitly named sections (e.g. %section ui) are entered as Some "ui"; the anonymous section is entered as None.

On Multiple Records in a Config File

There's no reason to use multiple refer records in a config file unless you tag them with different sections. Typically, a config file has one record per section. Since each record can have any number of fields, and any field can repeat, when you query a parsed config file for the field foo with find foo, you get back a list of values -- each value being one repetition of the requested field.

So this one-record config file:

%foo 1
%foo 2
%foo 3

Is the same as this two-record file:

%foo 1

%foo 2
%foo 3

Calling find "foo" in each case results in:

["1"; "2"; "3"]

However, given this two-record config file with two sections:

%section one
%foo 1

%section two
%foo 2
%foo 3

then find "foo" return [] (there is no anonymous section), find ~section:"one" "foo" returns ["1"], and find ~section:"two" "foo" returns ["2";"3"].

Validation

You can validate a config file at two levels:

For the first case, simply pass a compiled refer schema (Kwrefer.schemas) to Kwconfig.parse.

For the second case, use validation functions from Kwvalid and the Kwconfig.validate function.

Here's a schema for a simple config file with four fields:

%filename REQ UNIQ
%max OPT UNIQ
%development OPT UNIQ
%user OPT REP

If this schema lives in the file /etc/config.schema, and the config file is /etc/config, we can invoke Kwconfig.parse like so:

let schema = "/etc/config.schema" in
   parse ~schema:(schema, Kwrefer.compile_file schema) "/etc/config"

Supposing that %filename has to exist and be readable, that %max needs to be positive non-zero integer, that %development has to be a Boolean, and %user has to be a non-empty string, the following list will serve to validate the field values:

let validators = Kwvalid.([
  None, "filename", File.(existing && readable);
  None, "max", Num.W.any;
  None, "development", B.boolean;
  None, "user", String.notnull;
])

We can perform the field value validation like so:

validate validators config

Author(s): Keith Waclena


Types


module M: Kwmap.Make(sig
type t = string option 
val compare : 'a -> 'a -> int
end)
Maps keyed by optional sections.
type section = (string * string) list 
The type of sections, which are association lists.
type t = section M.t 
The type of parsed config files, which are Kwconfig.M's mapping optional section names to sections.

Exceptions


exception Badschema of string list
Errors encountered in a bad schema during attempted validation.
exception Invalid of string * string list
Errors that render a config file invalid.

Functions


val parse : ?schema:string * Kwrefer.schemas ->
?strict:bool ->
?section:string -> ?ignore:string list -> Kwchan.src -> t
parse ?schema ?section ?ignore chan: parse a config file on chan.

If you provide a schema, the config on chan will be validated against it; in this case, chan will be read twice: it can't be a pipe, and must be a Kwchan.Chan opened on a regular file, or a Kwchan.String.

schema : schema location (typically filename) and Kwrefer.schemas used to validate the config file
strict : whether or not to validate the config file in strict mode
section : the field that distinguishes different sections
ignore : section field values that are to be ignored (e.g. for comment sections)
chan : the channel containing the config file
val find : ?def:string list -> ?section:string -> string -> t -> string list
find ?def ?section key config: find key fields in parsed config file config.
def : default values for missing fields (default: [])
section : the section to consider (default: None)
key : the fieldname
val replace : ?section:string ->
string -> string -> t -> section M.t
replace ?section key value config: replace all mappings with key in section with (key,value).
Returns the new parsed config file
section : the section to consider (default: None)
key : the fieldname
value : the value
val section : M.key -> t -> section
section s config: extract the entire section s from the config in the form of a coalesced alist.

See Kwlist.coalesce.

s : the section
val fixup : (M.key * string * (string -> string)) list ->
t -> t
fixup sffs config: fixup certain config fields by selectively applying functions.

The fixup function is applied to the particular field value.

Example: translate ~'s in a filename field:

    fixup [None, "filename", Kwfile.squiggle ~respect_env:false] config

Returns the new parsed config file
sffs : list of fixup triples (section, key, fixup)
val validate : (string option * string * Kwvalid.t) list ->
t -> (string * string * string) list * t
validate validators config: validate field values in config as specified by validators.
Returns list of errors (key,value,what it isn't)
validators : list of validation triples (section, key, validator)
val defaults : (M.key * string * string list) list -> t -> t
defaults defs config: add default values for certain config fields.

Named fields in particular sections that are absent in config are inserted with default values.

Example: provide default value for filename field:

    defaults [None, "filename", ["/etc/default"]] config

Returns the new parsed config file
defs : list of defaults triples (section, key, defaults)
val init : ?schema:string * Kwrefer.schemas ->
?strict:bool ->
?section:string ->
?ignore:string list ->
?file:string ->
?string:string -> (M.key * string * string list) list -> t
init ?file ?string defs: initialize an app with a config file, setting defaults.

It's okay for ~file not to exist, in which case the defaults will apply.

schema : schema location (typically filename) and Kwrefer.schemas used to validate the config file
strict : whether or not to validate the config file in strict mode
section : the field that distinguishes different sections
ignore : section field values that are to be ignored (e.g. for comment sections)
file : the path to a config file
string : the literal string text of a config file
defs : list of defaults triples (section, key, defaults)