std/toml
Toml reads and writes any Encode/Decode type as TOML 1.0 or 1.1, and TomlValue is the tree of a document, with date and time values that keep their nanoseconds and their offset.
The generated reference of std/toml has every public construct with its signature, its doc comment and its examples.
std/toml is TOML as a format of std/encoding, written in TorbScript. Toml reads a document into any
Decode type and writes any Encode value, and TomlValue is the tree of a document, for one whose shape is not known
ahead of time. The reader takes every released 1.x - 1.0 and 1.1 - and passes the public
toml-test suite for both, which is vendored with its notice in
std/toml/tests; whatever the reader accepts as valid, the writer writes back as an equal tree.
Import#
use Toml, TomlVersion, TomlError from "std/toml"
use TomlValue, TomlEntry from "std/toml"
use TomlOffsetDateTime, TomlLocalDateTime, TomlDate, TomlTime from "std/toml"use Toml from "std/toml"
type Service {
name: String
replicas: Int = 1
ports: List<Int> = []
}
const toml = Toml()
match toml.decode<Service>("name = \"web\"\nports = [80, 443]\n") {
Ok(service) => {
match toml.encode(service) {
Ok(text) => print text
Fail(problem) => print "Not writable: {problem}"
}
}
Fail(problem) => print "Rejected: {problem}"
}replicas is not in the document, so it takes its default.
Declarations#
Toml#
type Toml {
naming: Naming = Naming.Unchanged
strict: Bool = false
version: TomlVersion = TomlVersion.Toml11
fn encode<Value: Encode>(value: Value): Result<String, TomlError>
fn decode<Value: Decode>(text: String): Result<Value, TomlError>
fn parse(text: String): Result<TomlValue, TomlError>
fn value<Value: Encode>(value: Value): Result<TomlValue, TomlError>
fn write(tree: TomlValue): String
}A Toml is the format with its options. naming spells every field name and strict makes a key of the document that
no type asked for an error, as they do for std/json and std/yaml. Toml has no Format
implementation and no framer: a TOML document is one table that is read whole, not a stream of items.
A document is a table. encode accepts a record or a map and refuses any other value with a TomlError, since a
number or a list is not a TOML document. TOML has no null: a field that holds None is left out, a key the document
does not have reads as None or as the field's default, and None inside a list is an error. A number beyond 64 bits
is an error as well, and bytes are written as a string of base64. A record that announces no field is its one value, a
case without fields is its name and a case with fields a table of one entry, its name.
decode reads one document. A failure is a TomlError: for text that is not TOML it has the line and the column, and
for a document that does not fit the type it has the chain of fields it went wrong in.
line 3, column 9: a value was expected here, and the line ends
ports[1]: a whole number is needed, not "http"parse reads the tree, write writes a tree back, and value is the tree an Encode value is written as. A
document is rejected where it defines a key or a table twice, extends a table that an inline table or an earlier
section closed, or gives an array of tables the name of a table.
TomlVersion#
type TomlVersion {
case Toml10
case Toml11
}version chooses what the writer may use, and nothing else: the reader accepts what is valid in either version. The
two differ in how a control character of a string is written, \uHHHH for Toml10 and \e or \xHH for Toml11, the
default. A document written for Toml10 can be read by every TOML 1.x reader; one for Toml11 needs a reader of 1.1.
The reader accepts what 1.1 adds in every document: an inline table over several lines and with a trailing comma, a time
without seconds, and the escapes \e and \xHH. A byte order mark in front of the document is skipped.
TomlValue#
type TomlValue with Show, Equals {
case String(value: String)
case Integer(value: Int)
case Float(value: Float64)
case Boolean(value: Bool)
case OffsetDateTime(value: TomlOffsetDateTime)
case LocalDateTime(value: TomlLocalDateTime)
case LocalDate(value: TomlDate)
case LocalTime(value: TomlTime)
case Array(items: List<Self>)
case Table(entries: List<TomlEntry>)
fn at(key: String): Self?
fn items(): List<Self>
fn entries(): List<TomlEntry>
fn isTable(): Bool
fn updated(key: String, value: Self): Self
fn equals(other: Self): Bool
fn show(): String
}
type TomlEntry with Show, Equals {
key: String
value: TomlValue
fn show(): String
}The tree of a document: what it says, in the order it says it. A table keeps its keys in the order of the document, and
the tree does not keep comments, the way a table was split into sections, or the kind of quotes a string used.
equals ignores the order of the keys of a table - TOML does not give it a meaning - and counts nan as equal to
nan, so a document and what the writer wrote of it are equal.
at(key) is the value of a key of a table, items() and entries() are the elements of an array and the entries of a
table (empty for any other value), and updated(key, value) is a table with one value set, kept in its place or added
at the end.
use Toml, TomlValue from "std/toml"
const toml = Toml()
match toml.parse("[server]\nhost = \"example.org\"\nport = 8080\n") {
Ok(tree) => {
const server = tree.at("server") ?? TomlValue.Table([])
print server.at("port")
print toml.write(tree.updated("title", TomlValue.String("demo")))
}
Fail(problem) => print "Rejected: {problem}"
}Dates and times#
type TomlDate with Show, Equals, Compare {
year: Int
month: Int
day: Int
static fn parse(text: String): Result<Self, DecodeError>
fn daysSinceEpoch(): Int
fn compare(other: Self): Ordering
fn show(): String
}
extend TomlDate with Encode, Decode
type TomlTime with Show, Equals, Compare {
hour: Int
minute: Int
second: Int = 0
nanosecond: Int = 0
static fn parse(text: String): Result<Self, DecodeError>
fn nanosecondsOfDay(): Int
fn compare(other: Self): Ordering
fn show(): String
}
extend TomlTime with Encode, Decode
type TomlLocalDateTime with Show, Equals, Compare {
date: TomlDate
time: TomlTime
static fn parse(text: String): Result<Self, DecodeError>
fn compare(other: Self): Ordering
fn show(): String
}
extend TomlLocalDateTime with Encode, Decode
type TomlOffsetDateTime with Show, Equals {
dateTime: TomlLocalDateTime
offsetMinutes: Int = 0
static fn parse(text: String): Result<Self, DecodeError>
fn toTimestamp(): Timestamp?
fn show(): String
}
extend TomlOffsetDateTime with Encode, DecodeTOML has four values for a point in time, and each is a type of its own so a field says which one it wants: a date
(1979-05-27), a time (07:32:00.5), a date and time without an offset (1979-05-27T07:32:00) and a date and time
with one (1979-05-27T07:32:00Z or 1979-05-27T00:32:00-07:00). They keep nanoseconds and the offset in minutes, and
show() is their RFC 3339 text. parse reads that text, with a space instead of the T too. A date that is not on
the calendar, such as February 30, is an error.
TomlOffsetDateTime.toTimestamp() is the point in time as a Timestamp of std/time, and None for one
beyond what a Timestamp holds.
In a document the four are TOML's own date and time values, not strings. They encode and decode as wrappers named by
their type, so a format that has no such value, JSON for one, writes the RFC 3339 text. A String field reads the
text of a date or time value as it was written.
use Toml, TomlOffsetDateTime from "std/toml"
type Release {
name: String
at: TomlOffsetDateTime
}
match Toml().decode<Release>("name = \"1.0\"\nat = 1979-05-27T00:32:00.5-07:00\n") {
Ok(release) => {
print release.at.show()
print release.at.toTimestamp()
}
Fail(problem) => print "Rejected: {problem}"
}TomlError#
type TomlError with Show, Error {
static fn syntax(line: Int, column: Int, message: String): Self
static fn decodeFailed(cause: DecodeError, line: Int = 0): Self
static fn unwritable(message: String): Self
fn isSyntaxError(): Bool
fn line(): Int
fn column(): Int
fn message(): String
fn show(): String
fn cause(): Error?
}What went wrong: text that is not TOML (syntax, with its line and column counted from 1), a document that does not fit
the type (decodeFailed, with the DecodeError that names the field chain) or a value that TOML cannot say
(unwritable). line() and column() are 0 where the problem belongs to none.
TomlEncoder#
type TomlEncoder with Encoder {
naming: Naming = Naming.Unchanged
fn result(): Result<TomlValue, TomlError>
var fn nothing()
var fn bool(value: Bool)
var fn int(value: Int64)
var fn unsigned(value: UInt64)
var fn float(value: Float64)
var fn decimal(value: Decimal)
var fn string(value: String)
var fn bytes(value: Bytes)
var fn sequence(length: Int?)
var fn map(length: Int?)
var fn record(typeName: String)
var fn variant(typeName: String, name: String)
var fn field(name: String)
var fn finish()
}TomlEncoder is the Encoder behind encode and value, exported for a format that builds on it. A value is
encoded into it, and result() is the tree, or the first thing that TOML cannot say.
Related#
- std/encoding -
Encode,DecodeandNaming, whichTomlimplements. - std/json - the same shape for JSON.
- std/yaml - the same shape for YAML.
- The standard library - the other packages.