moontoml TOML for MoonBit

Reference

API

Every public type, function and value. Signatures are the ones the compiler writes; the notes are the documentation comments in the source.

moonbitstack/moontoml

TOML 1.0.0: read a document into a Json tree, kind for kind, or as the document kept, and write it back. Every reader takes a flavor that names the edition, the preset being strict.

error Refused

Why a document was refused.

Redefined is the rule that matters most: §1 says a key may be defined only once, and so may a table, so a second definition is an error rather than a value that wins. It is the rule that makes a TOML file safe to append to.

pub(all) suberror Refused {
  Unexpected(At, Char)
  Truncated(At)
  BadEscape(At)
  BadNumber(At)
  BadDate(At)
  Redefined(At, String)
  MixedArray(At)
  Trailing(At)
  TooDeep(At)
  BadUtf8(At)
}
pub fn Refused::at(Self) -> At

Where the refusal happened.

Every variant carries a position, so a caller reporting a line does not have to match all ten of them to get at it.

struct At

Where in the source something is: the offset in code units, and the line and column a person counts from one.

pub(all) struct At {
  at : Int32
  line : Int32
  column : Int32
}

struct Doc

A document that remembers how it was written.

The other entry points read a TOML document into a tree and forget the page it came from. This one keeps the page: the source text, the items in the order they were read, each with the bytes it occupied, and the tree they denote. Change one value in place and write the result back, and the rest of the document is byte for byte as it was — the comments, the blank lines, the order, and the spelling of every value but the one that moved.

This is the reading for a file a person edits by hand — moon.mod, a service config, a manifest — where the diff should be the one line that changed.

pub(all) struct Doc {
  src : String
  mut items : Array[Item]
  root : Value
}
pub fn Doc::decode(StringView, flavor? : Flavor) -> Self raise Refused

Read a document into a Doc, which keeps it as it was written.

flavor names the edition, the same as the other entry points. A document the edition refuses is refused the same way decode refuses it.

pub fn Doc::get(Self, Array[String]) -> Value?

The value a path names, or nothing when the path does not name a value.

A path is the segments of a key as a person writes them: ["soil", "ph"] is the key under the table a [soil] header opens, and ["a", "b"] is the key written a.b.

pub fn Doc::render(Self) -> String

The document as it was read, with its changed values written back.

Nothing changed and the answer is the source byte for byte. A value that moved is written where it occupied, the way this module writes one; the key, the separator and anything after the value on the line are the original text, and so is everything between the lines — the comments, the blank lines, the order.

pub fn Doc::set(Self, Array[String], Value) -> Bool?

Change the value a path names, in place.

The path must name a value already in the document; when it names nothing — a missing key, or a table where a value should stand — the answer is nothing and the document is left as it was. Otherwise the value is replaced, the answer is whether it actually moved, and the line that held it is the only one written fresh when the document is written back.

pub fn Doc::value(Self) -> Value

The tree the document denotes — what decode answers for the same bytes.

enum Edition

Which edition of the format a document is read under.

The editions differ by real grammar: v04 has no dotted keys, no hex/octal/binary integers and no inf/nan; v05 adds those plus the three local date-time kinds; v100 is the current strict reading, and v110 relaxes inline tables to span lines with a trailing comma, adds the \x and \e escapes and lets a time drop its seconds.

pub(all) enum Edition {
  V04
  V05
  V100
  V110
}

struct Flavor

How a document is read.

The preset is strict: TOML 1.0.0 and a nesting limit of 500. Build another with Flavor::new, or update one in place with { ..strict, depth: 64 }. The version-specific presets live in the v04, v05 and v110 packages.

There is no knob for a repeated key. TOML 1.0.0 §1 makes redefinition an error, not a policy, so making it configurable would be offering to read a document the format says is not one.

pub(all) struct Flavor {
  depth : Int32
  edition : Edition
}
pub fn Flavor::new(depth? : Int32, edition? : Edition) -> Self

A flavor by name, every knob with the preset's value.

struct Item

One thing a document holds, in the order it was read: a table header or a key-value pair.

The four offsets say where the line and, for a pair, its value sat in the source. The document tiles exactly as the run of its items and the text between them, so writing it back over the source is the original byte for byte unless a pair's value has moved, in which case only that value is written fresh and the key, the separator and any trailing comment are kept as they were.

pub(all) struct Item {
  kind : ItemKind
  path : Array[String]
  start : Int32
  vstart : Int32
  vend : Int32
  end : Int32
  mut changed : Bool
}
pub fn Item::value_of(Self, Value) -> Value

The value an item of a document holds, walking the tree to its path.

enum ItemKind

The kind of thing an item of a document is.

pub(all) enum ItemKind {
  Header
  Pair
}

struct Real

A float, and the text it was written as.

TOML spells one number many ways — 1e06, 1000000.0, 1_000_000.0 — and a document read and written again should not turn one spelling into another. The text is kept whenever the source had one and it is also a JSON spelling, since that is what a tree of Json can carry; a float built in code has none and is written from its value.

pub(all) struct Real {
  value : Double
  repr : String?
}
pub fn Real::new(Double) -> Self

A float from its value alone.

enum Value

A TOML value, kind for kind.

A Json tree has no date-time and only one number, so loads reads a timestamp into text and a 64-bit integer into a double. decode reads the same document and keeps what the format says it is, which is what writing it back without guessing needs.

pub(all) enum Value {
  String(String)
  Integer(Int64)
  Float(Real)
  Boolean(Bool)
  Datetime(@moondate.Moment)
  Array(Array[Value])
  Table(Map[String, Value])
}
pub fn Value::array(Array[Self]) -> Self

An array value.

pub fn Value::boolean(Bool) -> Self

A boolean value.

pub fn Value::integer(Int64) -> Self

An integer value.

pub fn Value::string(String) -> Self

A string value.

pub fn Value::table(Map[String, Self]) -> Self

A table value, which is what a document is.

pub fn Value::to_json(Self) -> @json.Json

Functions

pub fn decode(StringView, flavor? : Flavor) -> Value raise Refused

The document in src, kind for kind.

TOML defines one document rather than a stream, so there is no plural form of this. This is the reading that keeps every distinction the format makes: a date is a date and not text, and an integer is 64 bits and not a double.

pub fn decode_bytes(BytesView, flavor? : Flavor, bom? : Bool) -> Value raise Refused

The document in src, decoded as UTF-8.

TOML 1.0.0 §1 says a file is UTF-8 and does not allow a byte-order mark; bom skips one anyway, because an editor on Windows writes it and refusing the file helps nobody. Pass bom=false to hold the format to its word.

pub fn decode_doc(StringView, flavor? : Flavor) -> Doc raise Refused

The document in src, kept as it was written.

This is the reading for a file a person edits: change a value in place and write the result back, and the rest of the document — the comments, the blank lines, the order, the spelling of every value but the one that moved — is the source byte for byte. See Doc.

pub fn dump(@json.Json, sort? : Bool) -> Bytes

The same, as UTF-8 bytes.

pub fn dumps(@json.Json, sort? : Bool) -> String

Write a Json tree as a TOML document.

Tables go under their own headers and scalars stay at the level they are on, so the output reads like a configuration file someone wrote rather than one a program emitted. An array of tables is written as [[…]] when every one of its elements is a table, and inline otherwise, which is the only honest choice: TOML has no way to spell an array mixing tables with scalars across headers.

sort orders keys by name; off, they keep the order the tree has. A document read by this module and written again carries the same values, but not necessarily the same layout — TOML can spell the same tree several ways.

A Json tree says "string" for what may have been a date-time, so this leans on the spelling: a string that is a date-time is written as one. encode writes a Value and needs no such guess.

pub fn encode(Value, sort? : Bool) -> String

Write a tree of Value as a TOML document.

Each value keeps the kind it has: a String is quoted even when it reads like a date, an Integer is written as the 64 bits it holds, and a Datetime is written bare, because turning it into a quoted string would change what it is.

pub fn encode_bytes(Value, sort? : Bool) -> Bytes

The same, as UTF-8 bytes.

pub fn load(BytesView, flavor? : Flavor, bom? : Bool) -> @json.Json raise Refused

The document in src, decoded as UTF-8, as a tree of Json.

TOML 1.0.0 §1 says a file is UTF-8 and does not allow a byte-order mark; bom skips one anyway, because an editor on Windows writes it and refusing the file helps nobody. Pass bom=false to hold the format to its word.

pub fn loads(StringView, flavor? : Flavor) -> @json.Json raise Refused

The document in src, as a tree of Json.

A date-time comes out as its RFC 3339 text and an integer as a Json number, because a Json tree has no place to keep either apart; decode is the reading that keeps them.

Values

pub let depth : Int32

How deep a document may nest.

500 is the preset: deep enough for real configuration, shallow enough that a hostile document cannot run the stack out.

pub let strict : Flavor

The preset: TOML 1.0.0 at the preset depth. The v04, v05 and v110 packages each fix a different edition and expose the same entry points.

moonbitstack/moontoml/v04

TOML 0.4: the same entry points, pinned to the 0.4 grammar — no dotted keys, no prefixed integers, no inf or nan, and only the offset date-time. Value, Flavor, Edition, At, Real, Refused and Doc are the root types, re-exported here.

Functions

pub fn decode(StringView) -> @moontoml.Value raise @moontoml.Refused

The document in src, read at TOML 0.4.

pub fn decode_bytes(BytesView, bom? : Bool) -> @moontoml.Value raise @moontoml.Refused

The document in src, read at TOML 0.4, decoded as UTF-8.

pub fn decode_doc(StringView) -> @moontoml.Doc raise @moontoml.Refused

The document in src, kept as it was written, read at TOML 0.4.

pub fn dump(@json.Json, sort? : Bool) -> Bytes
pub fn dumps(@json.Json, sort? : Bool) -> String
pub fn encode(@moontoml.Value, sort? : Bool) -> String
pub fn encode_bytes(@moontoml.Value, sort? : Bool) -> Bytes
pub fn load(BytesView, bom? : Bool) -> @json.Json raise @moontoml.Refused

The document in src, read at TOML 0.4, as a tree of Json, decoded as UTF-8.

pub fn loads(StringView) -> @json.Json raise @moontoml.Refused

The document in src, read at TOML 0.4, as a tree of Json.

Values

pub let edition : @moontoml.Edition

The edition this package reads at: every entry point below reads here.

moonbitstack/moontoml/v05

TOML 0.5: the same entry points, pinned to the 0.5 grammar — dotted keys, prefixed integers, inf and nan, and the local date-time kinds, with an array of one kind. The types are the root's, re-exported here.

Functions

pub fn decode(StringView) -> @moontoml.Value raise @moontoml.Refused

The document in src, read at TOML 0.5.

pub fn decode_bytes(BytesView, bom? : Bool) -> @moontoml.Value raise @moontoml.Refused

The document in src, read at TOML 0.5, decoded as UTF-8.

pub fn decode_doc(StringView) -> @moontoml.Doc raise @moontoml.Refused

The document in src, kept as it was written, read at TOML 0.5.

pub fn dump(@json.Json, sort? : Bool) -> Bytes
pub fn dumps(@json.Json, sort? : Bool) -> String
pub fn encode(@moontoml.Value, sort? : Bool) -> String
pub fn encode_bytes(@moontoml.Value, sort? : Bool) -> Bytes
pub fn load(BytesView, bom? : Bool) -> @json.Json raise @moontoml.Refused

The document in src, read at TOML 0.5, as a tree of Json, decoded as UTF-8.

pub fn loads(StringView) -> @json.Json raise @moontoml.Refused

The document in src, read at TOML 0.5, as a tree of Json.

Values

pub let edition : @moontoml.Edition

The edition this package reads at: every entry point below reads here.

moonbitstack/moontoml/v110

TOML 1.1: the same entry points, pinned to the 1.1 grammar — inline tables that span lines and end with a comma, the \x and \e escapes, and a time that may drop its seconds. The types are the root's, re-exported here.

Functions

pub fn decode(StringView) -> @moontoml.Value raise @moontoml.Refused

The document in src, read at TOML 1.1.

pub fn decode_bytes(BytesView, bom? : Bool) -> @moontoml.Value raise @moontoml.Refused

The document in src, read at TOML 1.1, decoded as UTF-8.

pub fn decode_doc(StringView) -> @moontoml.Doc raise @moontoml.Refused

The document in src, kept as it was written, read at TOML 1.1.

pub fn dump(@json.Json, sort? : Bool) -> Bytes
pub fn dumps(@json.Json, sort? : Bool) -> String
pub fn encode(@moontoml.Value, sort? : Bool) -> String
pub fn encode_bytes(@moontoml.Value, sort? : Bool) -> Bytes
pub fn load(BytesView, bom? : Bool) -> @json.Json raise @moontoml.Refused

The document in src, read at TOML 1.1, as a tree of Json, decoded as UTF-8.

pub fn loads(StringView) -> @json.Json raise @moontoml.Refused

The document in src, read at TOML 1.1, as a tree of Json.

Values

pub let edition : @moontoml.Edition

The edition this package reads at: every entry point below reads here.