module Kwconfig:sig..end
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.
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.
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"].
You can validate a config file at two levels:
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
module M:Kwmap.Make(sigtypet =string optionval compare :'a -> 'a -> intend)
typesection =(string * string) list
typet =section M.t
Kwconfig.M's mapping optional
section names to sections.exception Badschema of string list
exception Invalid of string * string list
val parse : ?schema:string * Kwrefer.schemas ->
?strict:bool ->
?section:string -> ?ignore:string list -> Kwchan.src -> tparse ?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 filestrict : whether or not to validate the config file in strict modesection : the field that distinguishes different sectionsignore : section field values that are to be ignored (e.g. for comment sections)chan : the channel containing the config fileval find : ?def:string list -> ?section:string -> string -> t -> string listfind ?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 fieldnameval replace : ?section:string ->
string -> string -> t -> section M.treplace ?section key value config: replace all mappings with key in section with (key,value).section : the section to consider (default: None)key : the fieldnamevalue : the valueval section : M.key -> t -> sectionsection s config: extract the entire section s from the config in the form of a coalesced alist.
See Kwlist.coalesce.
s : the sectionval fixup : (M.key * string * (string -> string)) list ->
t -> tfixup 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] configsffs : list of fixup triples (section, key, fixup)val validate : (string option * string * Kwvalid.t) list ->
t -> (string * string * string) list * tvalidate validators config: validate field values in config as specified by validators.validators : list of validation triples (section, key, validator)val defaults : (M.key * string * string list) list -> t -> tdefaults 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"]] configdefs : 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 -> tinit ?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 filestrict : whether or not to validate the config file in strict modesection : the field that distinguishes different sectionsignore : section field values that are to be ignored (e.g. for comment sections)file : the path to a config filestring : the literal string text of a config filedefs : list of defaults triples (section, key, defaults)