This is a package for the Milo language.
Read TOML config files, and write them back out.
let port = cfg.i64Path("server.port") ?? 8080
A missing key comes back as None, so ?? supplies the default in the same
line.
All of TOML v1.0.0, strictly: duplicate keys and duplicate table headers are errors here, not last-wins. Full API: docs/api.md.
milo add github.com/milo-language/milo-tomlfrom "toml" import { Toml }
Given app.toml:
name = 'checkout-api'
debug = false
[server]
host = '0.0.0.0'
port = 8080from "toml" import { Toml }
from "std/fs" import { readFile }
pub fn main(): i32 {
let cfg = Toml.parse(readFile("app.toml")!)!
let name = cfg.str("name") ?? "unnamed" // top-level string
let debug = cfg.bool("debug") ?? false // top-level bool
let port = cfg.i64Path("server.port") ?? 8080 // dotted path into [server]
let ratio = cfg.f64Path("limits.ratio") ?? 1.0 // absent, so the default answers
print($"{name} debug={debug} port={port} ratio={ratio}")
// A sub-table can be pulled out and reused as its own handle.
let server = cfg.table("server")!
print($"{server.str("host") ?? "127.0.0.1"}:{server.i64("port") ?? 80}")
return 0
}
checkout-api debug=false port=8080 ratio=1
0.0.0.0:8080
[[route]] blocks come back as an array, in document order. Arrays and tables
are both handles, so the same len/at walk reaches either:
let cfg = Toml.parse("
tags = ['web', 'api']
[[route]]
path = '/health'
methods = ['GET']
[[route]]
path = '/orders'
methods = ['GET', 'POST']
")!
let tags = cfg.get("tags")!
for i in 0..tags.len() {
print(tags.at(i)!.asStr() ?? "")
}
let routes = cfg.get("route")!
for i in 0..routes.len() {
let r = routes.at(i)!
print(r.str("path") ?? "?")
let methods = r.get("methods")!
for m in 0..methods.len() {
print(" " + (methods.at(m)!.asStr() ?? ""))
}
}
web
api
/health
GET
/orders
GET
POST
stringify renders a document back to text that re-parses to an equal document,
and rendering that again is byte-identical:
let doc = Toml.parse("
[server]
port = 30_000
host = 'localhost'
")!
let text = doc.stringify()
let again = Toml.parse(text.clone())!
print(text)
print($"round-trips: {text == again.stringify()}")
[server]
port = 30000
host = "localhost"
round-trips: true
Formatting is not preserved, which the output above shows: comments are dropped,
30_000 comes back as 30000, and 'literal' strings come back
double-quoted. Key order, values, types and structure are preserved.
A complete program against a real service config:
milo run examples/config.milo examples/app.toml.
Node handles and string-pool handles were both bare i64, so assigning one where the
other belonged compiled silently and produced a link into the wrong pool. They are now
distinct zero-cost newtypes, NodeId and StrId, and the checker separates the two
index spaces.
This changes the public cursor API: curRoot, curChild, curField and curPath
return NodeId, and every cur* accessor takes one. Callers that stored a cursor as
i64 import NodeId and use that type instead — the values and the layout are
unchanged (a single-field struct compiles to exactly its field), so only the spelling
moves.