SCUA reads the common config formats out of the box.
toml and ini are pure modules
— parsing text needs no capability grant — and both parse to a
nested table, which is the whole point: the result
pairs directly with first-class
paths, so you read config with cfg @ "server/port"
instead of a chain of field lookups.
(JSON is also built in — see json in the builtins
reference. For YAML, see "What's not here yet" below.)
#TOML
import toml
let text = "
[server]
host = \"0.0.0.0\"
port = 8080
debug = true
"
match toml.parse(text)
Ok(cfg) -> do
print(cfg @ "server/host") -- "0.0.0.0"
print(cfg @ "server/port") -- 8080 (a real int)
print(cfg @ "server/debug") -- true (a real bool)
print(cfg @ "server/region" ?? "us-east") -- missing key → default
end
Error(why) -> print(`bad config: {why}`)
end
toml.parse(s) returns Ok(table) or
Error(reason) — a malformed document is a value you handle,
not a crash. Sections nest (a dotted [a.b] header nests two
levels), and scalar values keep their type (int,
bool, string).
Today this is the config subset of TOML:
[sections]and string/int/bool keys — which covers the overwhelming majority of real config files. Arrays, floats, inline tables, and datetimes are not yet supported and parse to anError. Full TOML 1.0 is a planned follow-up.
#INI
import ini
let text = "
[db]
host = db.internal
port = 5432
"
match ini.parse(text)
Ok(cfg) -> do
print(cfg @ "db/host") -- "db.internal"
print(tonumber(cfg @ "db/port")) -- 5432
end
Error(why) -> print(`bad config: {why}`)
end
INI has no value types, so every value is text — use
tonumber where you want a number. [section]
headers nest (dotted names like [db.pool] nest further),
# and ; start full-line comments, and both
key = value and key: value are accepted.
#Reading from a file
The examples above use inline strings so they're self-contained. In a
real tool you'd read the file with the fs capability and feed
it straight in — the ? operator threads the read failure
through:
import fs
import toml
fn load_config()
let text = fs.read_text("config.toml")? -- Error short-circuits if the file is missing/!UTF-8
return toml.parse(text) -- Ok(table) | Error(reason)
end
Run it with scua --allow-fs=. tool.scua.
#Why paths
Because the parsers return nested tables, the same @
path access, set, and path"..." values that
work on any table work on parsed config — and a missing key reads as
nil, so ?? gives you a default in one line.
See the runnable examples/config.scua.
#What's not here yet
- YAML — deliberately deferred. Full YAML is large and ambiguous (anchors, tags, the "Norway problem"); a safe, strictly-bounded subset is planned but its exact boundary needs pinning first. Until then, prefer TOML or JSON for new config.
- Writing/serializing config
(
toml.encode/ini.encode) — parse-only for now. - Full TOML 1.0 (arrays, floats, inline tables, datetimes) — a planned follow-up.