Configuration files, their expansion, their layering and their binding to a typed model.
test {
let node = @moonconf.Node::map([
("db", @moonconf.Node::map([("host", @moonconf.Node::str("localhost", "app.ini"))], "app.ini")),
], "app.ini")
assert_eq(node.need_text("db.host"), "localhost")
}test {
let node = @moonconf.Node::map([
("servers", @moonconf.Node::seq([
@moonconf.Node::str("one", "app.conf"),
@moonconf.Node::str("two", "app.conf"),
], "app.conf")),
], "app.conf")
assert_eq(node.need_text("servers.1"), "two")
}test {
let defaults = @moonconf.Node::map([
("port", @moonconf.Node::num(5432, "defaults")),
], "defaults")
let file = @moonconf.Node::map([
("port", @moonconf.Node::num(6432, "app.ini")),
], "app.ini")
let merged = @moonconf.merge([
@moonconf.Source::new(@moonconf.File, "app.ini", file),
@moonconf.Source::new(@moonconf.Default, "defaults", defaults),
])
assert_eq(merged.need_int("port"), 6432L)
}| Package | Reads | Writes | Knobs |
|---|---|---|---|
| moonconf/ini | sections, = or :, continuations, defaults, two interpolations | yes | standard, extended, literal |
| moonconf/props | Java properties, ISO-8859-1 and escapes | yes | — |
| moonconf/dotenv | .env with quotes, escapes, continuation and references | yes | interpolated, verbatim |
| moonconf/expand | POSIX parameter expansion | — | strict, lenient, deferred |
| moonconf/hocon | JSON with relaxed punctuation, merging, references, includes | — | ambient, plain, with_includes, with_fallback |
| moonconf/hcl | HCL 2 native and JSON syntax, evaluated against a context | — | Context, standard |
[DEFAULT]
host = localhost
[db]
port = 5432
url = postgres://%(host)s/dbtest {
let doc = @ini.parse("[db]\nport = 5432\nurl = postgres://localhost/db\n")
assert_eq(doc.get("db", "url"), "postgres://localhost/db")
assert_eq(doc.sections(), ["db"])
}# a note
db.host=localhost
greeting=\u4f60\u597dtest {
let doc = @props.parse("db.host=localhost\n")
assert_eq(doc.get("db.host"), Some("localhost"))
}export HOST=localhost
DSN="postgres://${HOST}/app"
GREETING='it\'s here'test {
let env = @dotenv.load("HOST=localhost\nDSN=\"postgres://${HOST}/app\"\n")
assert_eq(env.get("DSN"), Some("postgres://localhost/app"))
}test {
let vars = @expand.Vars::of_map(Map([("PORT", "8080")]))
assert_eq(@expand.expand(":${PORT:-80}", vars), ":8080")
assert_eq(@expand.expand("${MISSING:-fallback}", vars), "fallback")
}include "base.conf"
db.name = app
url = "postgres://${db.host}:${db.port}/${db.name}"
hosts = [a, b]
hosts += c
cache = ${?optional}test {
let doc = @hocon.parse("db { host = localhost }\nurl = \"pg://${db.host}/x\"\n")
assert_eq(doc.need_text("url"), "pg://localhost/x")
}region = var.region
name = upper("web")
server "web" {
port = 80 + 1
}test {
let names = @moonconf.Node::map([("region", @moonconf.Node::str("eu-west-1", "vars"))], "vars")
let body = @hcl.parse("region = var.region\n", context=@hcl.Context::new().bind("var", names))
assert_eq(body.get("region").unwrap().text(), "eu-west-1")
}test {
let model = @moonmodel.Model::new("Db", [
@moonmodel.Field::new("host", @moonmodel.Type::Text, required=true),
@moonmodel.Field::new("port", @moonmodel.Type::Int, required=true),
@moonmodel.Field::new("pool", @moonmodel.Type::Int, default=Json::number(4.0)),
])
let tree = @moonconf.Node::map([
("host", @moonconf.Node::str("localhost", "app.ini")),
("port", @moonconf.Node::str("5432", "env")),
], "app.ini")
assert_eq(
@moonconf.bind(tree, model),
@json.parse("{\"host\":\"localhost\",\"port\":5432,\"pool\":4}"),
)
}db.port (env@1:1): want integer, got text| Function | Meaning |
|---|---|
| merge(sources, knobs?) | Merge layers into one tree |
| Node::merge(base, over, clash?=Last) | Merge two trees |
| Node::find(path) / need(path) | The value at a dotted path |
| Node::need_text/int/bool/real/list/table(path) | The value, as a type |
| bind(node, model, known?, knobs?) | Check against a model, as JSON |
| validate(node, model, known?, knobs?) | Every refusal, not just the first |
| native(node) | The tree as JSON, without provenance |
| of_json/of_yaml/of_toml(text, source?) | Read a borrowed format into the tree |
| Function | Meaning |
|---|---|
| expand(text, vars, knobs?) | Expand a text in the shell's parameter syntax |
| Vars::of(get) / Vars::writing(get, set) / Vars::of_map(map) | Where names come from |
| Function | Meaning |
|---|---|
| parse(text, knobs?) | Read a document |
| Doc::get(section, option) / raw / get_or | The value, interpolated or not |
| Doc::sections() / options(section) / has_option | What is in it |
| Doc::tree(source?) | The document as the shared tree |
| dumps(doc) | Write it back |
| Function | Meaning |
|---|---|
| parse(text) / load(bytes) | Read text, or bytes as ISO-8859-1 |
| Doc::get(key) / keys() / items() | The pairs |
| Doc::tree(source?) | The file as the shared tree |
| dumps(doc, comment?) | Write it back |
| Function | Meaning |
|---|---|
| parse(text, source?) | The entries, and the lines that were not ones |
| load(text, vars?, knobs?, source?) | The variables, references expanded |
| tree(text, vars?, knobs?, source?) | The file as the shared tree |
| dumps(entries) | Write it back |
| Function | Meaning |
|---|---|
| parse(text, knobs?, source?) | Read a document into the shared tree |
| Knobs::with_read(read) / with_includes(from) | Where an include comes from, with or without its wrapper; the environment stays the fallback |
| Knobs::with_fallback(lookup) | Replace where an unresolved name is looked up |
| Function | Meaning |
|---|---|
| parse(text, context?, source?) | Read the native syntax |
| parse_json(text, source?) | Read the JSON syntax |
| Body::get(name) / blocks_of(kind) / tree(source) | What is in a body |
| Context::new() / bind(name, value) / function(name, body) | Names and functions in scope |
| standard() | The functions a configuration file usually needs |
pub(all) suberror Refused {
Type(want~ : String, got~ : String, path~ : String, source~ : String, at~ : At)
Missing(path~ : String, want~ : String)
Unknown(path~ : String, got~ : String)
Clash(path~ : String, first~ : String, second~ : String)
Path(got~ : String, at~ : Int)
Format(why~ : String, at~ : At?)
TooDeep(at~ : At, limit~ : Int)
}fn whole(text : StringView) -> Int64?Install
Download zipConfiguration files, their expansion, their layering and their binding to a typed model.