toml.dart

Dart CI Coverage Status Pub Package GitHub License

This package provides an implementation of a TOML parser and encoder for Dart.

It currently supports version 1.0.0 of the TOML specification.

Table of Contents

  1. Installation
  2. Usage
    1. Loading TOML
    2. Parsing TOML
    3. Decoding TOML
    4. Encoding TOML
  3. Data Structure
    1. Table and Inline Table
    2. Array and Array of Tables
    3. String
    4. Integer
    5. Float
    6. Boolean
    7. Offset Date-Time
    8. Local Date-Time
    9. Local Date
    10. Local Time
  4. Testing
  5. License

Installation

To get started add toml as a dependency to your pubspec.yaml and run the dart pub get or flutter pub get command.

dependencies:
  toml: "^0.11.0"

Usage

The toml.dart package can be used for loading, decoding and encoding TOML documents. To get started, just add the following import.

import 'package:toml/toml.dart';

The subsequent sections describe how to use the library in more detail. Additional examples can be found in the ./example directory.

Loading TOML

In order to load a TOML document, invoke the static TomlDocument.load method and pass the name of the configuration file to load. The method returns a Future of the loaded TomlDocument.

void main() async {
  var document = await TomlDocument.load('config.toml');
  // ...
}

When the code is running in the browser, HTTP is used to fetch the configuration file. When the code is running on the Dart VM or natively, the file is loaded from the local file system.

If the loaded TOML file contains a syntax error, a TomlParserException is thrown.

Full examples for loading a configuration file via HTTP and from a file can be found in ./example/http_config_loader and ./example/filesystem_config_loader, respectively.

Parsing TOML

Sometimes the two default mechanisms for loading TOML documents with TomlDocument.load are not sufficient. In those cases, you can simply load the contents of the configuration file yourself and parse them as a TOML document manually using the static TomlDocument.parse function.

void main() {
  var contents = '...';
  var document = TomlDocument.parse(contents);
  // ...
}

If the loaded TOML file contains a syntax error, a TomlParserException is thrown.

An example for writing a custom configuration file loader and parsing the loaded file manually can be found in ./example/toml_parser.

Decoding TOML

In the last two subsections we've learned how to load or parse a TomlDocument. Such a TomlDocument is an abstract representation of the syntax of a TOML document. In order to access the configuration options that are stored in the TOML document, we first have to convert it to a hash map with the TomlDocument.toMap method.

void main() {
  var config = TomlDocument.parse('...').toMap();
  // ...
}

If the TOML document is semantically invalid, a TomlException is thrown.

In the next section the type and structure of the generated hash map will be elaborated in more detail.

Encoding TOML

This package also includes a TOML encoder that can convert a hash map to a TOML document. Simply use the TomlDocument.fromMap factory constructor to convert the hash map into the internal representation. The resulting document can be converted to a TOML encoded string using the toString method.

var document = TomlDocument.fromMap({
  // ...
}).toString();

The type and structure of the hash map should match the format described in the next section. Additionally, the map may contain arbitrary keys and values that implement the TomlEncodableKey and TomlEncodableValue interfaces, respectively. Classes which implement those interfaces must define a toTomlKey or toTomlValue method, whose return value is either implements the interface itself or is natively encodable by TOML. If an object cannot be encoded as a TOML key or value, a TomlUnknownKeyTypeException or TomlUnknownValueTypeException is thrown by TomlDocument.fromMap.

An example for using the encoder and the TomlEncodableValue interface to encode a Map can be found in ./example/toml_encoder.

Data Structure

This section describes how the decoder (i.e., TomlDocument.toMap) maps TOML values to Dart values and which Dart values are accepted by the encoder (i.e., TomlDocument.fromMap).

Table and Inline Table

TOML documents and tables including inline tables are represented through nested Map objects whose keys are Strings and values dynamic representations of the corresponding TOML value or sub-table.

For example, given the following TOML document

[parent.table]
key = 'value'

the decoder produces the following Map.

<String, dynamic>{
  'parent': <String, dynamic>{
    'table': <String, dynamic>{'key': 'value'}
  }
}

The encoder does not require all keys to have the static type String. A map of type Map<MyKey, dynamic> can be encoded, provided that the class MyKey implements the TomlEncodableKey interface. The encoder never produces inline-tables or dotted keys at the moment.

Array and Array of Tables

The decoder produces List objects for all kinds of arrays including arrays of tables. For example, given the following TOML document

[[items]]
name = 'A'

[[items]]
name = 'B'

[[items]]
name = 'C'

the decoder produces the following List.

<String, dynamic>{
  'items': [
    <String, dynamic>{'name': 'A'},
    <String, dynamic>{'name': 'B'},
    <String, dynamic>{'name': 'C'},
  ]
}

The encoder accepts any Iterable.

String

All string variants produce regular dart Strings. For example, all strings in the listing below map to the Dart value 'Hello, World!'.

str1 = "Hello, World!"
str2 = 'Hello, World!'
str3 = """\
  Hello, \
  World!\
"""
str4 = '''
Hello, World!'''

The encoder preferably generates literal strings. If a string contains apostrophes or other characters that are not allowed in literal strings, a basic string is produced instead. Strings that contain newlines produce multiline strings. Whether a multiline literal or multiline basic string is generated depends on the remaining characters in the string. All produced multiline strings start with a trimmed newline and multiline basic strings never contain escaped whitespace.

If you need more control over the type of string that is produced by the encoder, you can wrap the value with a TomlLiteralString, TomlBasicString, TomlMultilineLiteralString or TomlMultilineBasicString.

Integer

Integers are represented by ints or BigInts.

The decoder produces an int only if the number can be represented losslessly by an int. Whether a number can be represented by an int is platform specific. When the code is running in the VM, numbers between -2^63 and 2^63 - 1 can be represented as an int. In JavaScript, only numbers in the range from -(2^53 - 1) to 2^53 - 1 are guaranteed to be converted to int but smaller or larger numbers may still produce an int if they can be represented without loss of precision.

The encoder accepts either representation and uses the decimal integer format by default. If you need more control over the format used by the encoder, the value has to be wrapped in a TomlInteger.

For example, the following map

<String, dynamic>{
  'decimal': 255,
  'hexadecimal': TomlInteger.hex(255)
}

is encoded as shown below.

decimal = 255
hexadecimal = 0xff

Float

Floating point numbers are represented as doubles. The special floating point values inf, -inf and nan are mapped to double.infinity, double.negativeInfinity and double.nan, respectively. The value -nan is also mapped to double.nan.

When compiled to JavaScript, a double without decimal places cannot be distinguished from an int. Thus, the encoder produces an integer for double values without decimal place. If you want to force a JavaScript number to be encoded as a float, it has to be wrapped in a TomlFloat.

Boolean

Boolean values are represented as values of type bool. The encoder produces the TOML values true and false.

Offset Date-Time

Offset Date-Time values are represented by the TomlOffsetDateTime class. They can be converted to a UTC DateTime object with the toUtcDateTime method.

The encoder accepts TomlOffsetDateTime as well as DateTime objects. A UTC DateTime is encoded as a offset date-time in the UTC time-zone. A local DateTime is encoded as a offset date0time in the local time-zone. For example, the following dates

<String, dynamic>{
  'utc': DateTime.utc(1969, 7, 20, 20, 17),
  'local': DateTime(1969, 7, 20, 20, 17),
}

are encoded as shown below when the code runs in the UTC+01:00 time-zone.

utc = 1969-07-20 20:17:00Z
local = 1969-07-20 20:17:00+01:00

Local Date-Time

Local Date-Time values are represented by the TomlLocalDateTime class. Before they can be converted to a DateTime, you must provide a time-zone information in which to interpret the date-time. For example, to interpret a local date-time in the time-zone the code is running in, type the following.

localDateTime.withOffset(TomlTimeZoneOffset.local()) // interpret in local time-zone

To interpret the local date-time value in a specific time-zone, write the following for example.

localDateTime.withOffset(TomlTimeZoneOffset.positive(1, 0)) // interpret in UTC+01:00

In both cases the result is a TomlOffsetDateTime that can be converted to a DateTime as described above. The encoder accepts TomlLocalDateTime objects only, i.e., a DateTime will never be encoded as a local date-time automatically.

Local Date

Local Date values are represented by the TomlLocalDate class. Before they can be converted to a DateTime, both a time and time-zone offset have to be provided. First add a time with the atTime method and use withOffset from the resulting TomlLocalDateTime to obtain a offset date-time that can then be converted to a DateTime.

localDate.atTime(time).withOffset(timeZoneOffset)

Local Time

Local Time values are represented by the TomlLocalTime class. Before they can be converted to a DateTime, both a date and time-zone offset have to be provided. First add a date with the atDate method and use withOffset from the resulting TomlLocalDateTime to obtain a offset date-time that can then be converted to a DateTime.

localTime.atDate(date).withOffset(timeZoneOffset)

Testing

To see whether everything is working correctly change into the root directory of this package and run the included tests as follows:

dart test

You may pass the --platform command line argument to test the package on other platforms than the VM. Run dart test --help for a list of all available platforms.

Alternatively, you can run toml-test (again from the package root):

$GOPATH/bin/toml-test bin/decoder.dart
$GOPATH/bin/toml-test -encoder bin/encoder.dart

To speedup the tests, it is recommended to compile the encoder and decoder scripts before running toml-test.

mkdir -p build/bin
dart compile exe -o build/bin/decoder bin/decoder.dart
dart compile exe -o build/bin/encoder bin/encoder.dart

$GOPATH/bin/toml-test build/bin/decoder
$GOPATH/bin/toml-test -encoder build/bin/encoder

Unfortunately, toml-test supports version 0.4.0 of the TOML specification only such that some tests will fail.

License

toml.dart is licensed under the MIT license agreement. See the LICENSE file for details.

Libraries

toml
This library exports the entire public interface of the toml.dart package.