TOML
The toml module reads TOML 1.0 into a
TomlValue, lets you look values up and change them, writes tables back
as documents, edits a document in place without disturbing its comments,
and converts structs to TOML and back. The package manager reads and
edits pep.toml and pep.lock with it.
# TOML: parse a document, look values up, edit it in place keeping its
# comments, and read it into a struct.
import toml;
struct Package {
[Required]
var name: String;
var version: String;
}
struct Manifest {
[Required]
var package: Package;
}
main(): int {
var text = "[package]\nname = \"demo\" # the name\nversion = \"0.1.0\"\n";
var parsed = toml.parse(text, path = "pep.toml");
if (parsed.is_err()) {
print(parsed.error().to_string()); # pep.toml:2:6: error: ...
return 1;
}
var root = parsed.value();
assert(root["package"]["name"].as_string().value() == "demo", "a string");
assert(root.lookup("package.version").value().as_string().value() == "0.1.0", "a dotted lookup");
assert(root.try_get("dependencies").is_empty(), "a missing key");
# Change a value, then write the tables back as a document.
var package = root["package"];
package["edition"] = toml.TomlValue.Integer(2026);
assert(root.to_document() == "[package]\nname = \"demo\"\nversion = \"0.1.0\"\nedition = 2026\n", "a document");
# Edit the text in place: comments and layout stay as written.
var document = toml.parse_document(text, path = "pep.toml").value();
document.set(["dependencies", "yaml"], toml.TomlValue.String("0.3")).value();
assert(
document.to_string() == "[package]\nname = \"demo\" # the name\nversion = \"0.1.0\"\n\n[dependencies]\nyaml = \"0.3\"\n",
"an edit"
);
# Read it into a struct, with the serialize attributes json uses.
Manifest manifest = toml.decode<Manifest>(text).value();
assert(manifest.package.version == "0.1.0", "a struct");
# An error names the line and column.
var bad = toml.parse("name \"demo\"\n", path = "bad.toml");
assert(bad.error().to_string() == "bad.toml:1:6: error: expected '=' after the key", "where");
return 0;
}
Values
toml.TomlValue is an enum whose cases carry values:
| Case | Holds | TOML |
|---|---|---|
String(value: String) | the string | "hi", 'C:\path', """...""" |
Integer(value: long) | a 64-bit integer | 42, 0xff, 1_000 |
Float(value: double) | a float, inf and nan included | 0.5, 1e10, inf |
Boolean(value: bool) | the boolean | true, false |
DateTime(value: TomlDateTime) | a date, a time, or both | 1979-05-27T07:32:00Z, 1979-05-27, 07:32:00 |
Array(items: Array<TomlValue>) | the elements | [1, "two"] |
Table(members: OrderedMap<String, TomlValue>) | the keys, in the order written | [server], { x = 1 } |
- Look values up with
value["key"]andvalue[index], which panic on a miss, ortry_get("key")andlookup("package.name"), which give an emptyOptional. Theas_methods (as_string,as_int,as_float,as_bool,as_date_time,as_array,as_table) give an emptyOptionalfor another kind;as_floatalso reads an integer. - Write a value on one line with
to_string()(as after a key’s=), or a table as a whole document withto_document(). - There is no null. What
jsonwrites asnullis left out, and a missing key reads as the property’s default.
Errors
toml.parse(text, path = "pep.toml") checks every rule of TOML 1.0 and
gives a TomlError that renders like a compiler error:
pep.toml:4:12: error: expected '=' after the key. Arrays and inline
tables may nest max_depth deep (1000 by default); parsing and writing
use explicit stacks, so deep nesting never overflows the stack. The
module passes all 709 TOML 1.0 cases of
toml-test, which its tests run.
Editing a document in place
toml.parse_document(text) gives a TomlDocument, whose set(keys, value) and remove(keys) change only the lines they must, keeping every
comment and blank line, the way cargo add edits Cargo.toml. A changed
value is rewritten on one line; a new key goes after the last key of its
table, indented like it; a new table goes at the end as a [header]
section, or as { ... } with inline = true. Edits cannot reach inside an
array of tables ([[bin]]); set or remove the whole array.
Structs
toml.encode(value) and toml.decode<T>(text) (and to_toml and
from_toml, which work on a TomlValue) convert structs through
reflection, with the serialize
attributes JSON uses: [Name("key")], [Ignore] and
[Required]. unknown_keys = toml.UnknownKeys.Reject makes an unknown key
an error. See the reference for every
function.