Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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:

CaseHoldsTOML
String(value: String)the string"hi", 'C:\path', """..."""
Integer(value: long)a 64-bit integer42, 0xff, 1_000
Float(value: double)a float, inf and nan included0.5, 1e10, inf
Boolean(value: bool)the booleantrue, false
DateTime(value: TomlDateTime)a date, a time, or both1979-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"] and value[index], which panic on a miss, or try_get("key") and lookup("package.name"), which give an empty Optional. The as_ methods (as_string, as_int, as_float, as_bool, as_date_time, as_array, as_table) give an empty Optional for another kind; as_float also reads an integer.
  • Write a value on one line with to_string() (as after a key’s =), or a table as a whole document with to_document().
  • There is no null. What json writes as null is 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.