toml
std/toml — reading and writing TOML.
Written in Kite, on the same shape as std/json: a parse returns (Toml, error), and a malformed document is a value to handle rather than something thrown. Positions are threaded through as (value, next) pairs instead of a cursor with a var field, so a parse cannot be left half-advanced by an early return.
The subset is named rather than implied. What is here is what a configuration file uses and what kite.toml needs:
- comments, to end of line
- bare and quoted keys, and dotted keys —
a.b.c = 1 [table]and[a.b]headers, and[[array.of.tables]], which emit
back out in the same form they were read
- basic strings with the standard escapes including
\uXXXXand
\UXXXXXXXX, and literal '…' strings
- multi-line
"""…"""and'''…'''strings - integers, including
+/-signs and_separators - floats, including exponents.
infandnanare refused: Kite has no
literal for either, and dividing to reach one is a trap
- booleans
- arrays, which may span lines and may hold anything
- inline tables —
{ a = 1, b = 2 }
What is not here, and would be wrong to guess at: dates and times. TOML's offset date-time, local date-time, date and time are four distinct types, and Kite has time.Instant and time.Date to map two of them onto — so the mapping is a decision about std/time's surface rather than about parsing, and it is left undone rather than half-done. A date in a document parses as the string it was written as, which is lossless and honest, and is stated here so nobody discovers it from a wrong answer.
Toml
enum Toml
A TOML value.
Recursive with no boxing annotation, because every Kite aggregate is already a GC reference.
Bool(bool)Int(int)Float(float)Text(str)Array([Toml])Table({ str: Toml })
parse
pub fn parse(input: str) -> (Toml, error)
The table a document holds, or an error saying where it went wrong.
The top level of a TOML document is always a table, which is the one way it differs from JSON at the root.
at
pub fn at(doc: Toml, path: str) -> Option<Toml>
The value at a dotted path, or nil.
toml.at(doc, "package.name") rather than three lookups and two matches, because reading a configuration file is the thing this module is for.
text_at
pub fn text_at(doc: Toml, path: str, fallback: str) -> str
The string at a path, or fallback if it is absent or is not a string.
int_at
pub fn int_at(doc: Toml, path: str, fallback: int) -> int
The integer at a path, or fallback.
float_at
pub fn float_at(doc: Toml, path: str, fallback: float) -> float
The float at a path, or fallback.
An integer answers too. TOML distinguishes 1 from 1.0 and a caller asking for a float has already said which it wants, so refusing to widen would fail on a configuration that simply wrote a round number.
bool_at
pub fn bool_at(doc: Toml, path: str, fallback: bool) -> bool
The boolean at a path, or fallback.
emit
pub fn emit(doc: Toml) -> str
A document, written back out.
Scalars first and tables afterwards, which is not a style preference: a key written after a [table] header belongs to that table, so a top-level key emitted after one would be read back into the wrong place. Emitting in this order is what makes parse(emit(doc)) give back doc.