The Config API provides encoding and decoding functionality for moving data between Config text and Tiri tables.
Config text is a simple, human-readable data format built from named groups of key-value pairs. It is the same format used by the Config class, so this library is a convenient way to produce and consume that data directly in script without instantiating an object.
The API can be loaded with the line:
import 'config'Config data is represented as a table of group tables. Each top-level key names a group, and each group is itself a table of string-valued keys:
data = {
Display = { Width = '1280', Height = '720' },
Audio = { Volume = '80' }
}This corresponds to the following Config text:
[Audio]
Volume = 80
[Display]
Height = 720
Width = 1280A few properties of the format are worth noting:
8 or Yes are returned verbatim as '8' and 'Yes'; no type conversion is performed.#), source ordering, and incidental formatting are not preserved across a decode.[Display] and [display] are distinct groups.result = config.decode(String)
Decodes Config text into a table of groups and key-value tables.
data = config.decode('[Display]\nWidth = 1280\n')
-- Result: { Display = { Width = '1280' } }The parser is deliberately lenient and mirrors the behaviour of the Config class:
\n) and CRLF (\r\n) line endings are accepted.#) are skipped.=. Surrounding whitespace is trimmed from the key, and leading whitespace is trimmed from the value. Trailing whitespace in a value is preserved.=, are ignored.[Invalid with no closing bracket, or a nested [) closes the current group, so subsequent keys are discarded until the next valid header.source = [[
# Font definitions
[Clean]
Bold = fonts:fixed/clean.fon
Points = 8
Styles = Bold,Bold Italic,Italic,Regular
]]
data = config.decode(source)
-- data.Clean.Bold = 'fonts:fixed/clean.fon'
-- data.Clean.Points = '8' (still a string)
-- data.Clean.Styles = 'Bold,Bold Italic,Italic,Regular'text = config.encode(Value)
Encodes a table of groups and key-value tables as deterministic Config text.
text = config.encode({ Display = { Width = 1280 } })
-- Result: '[Display]\nWidth = 1280\n'The output is canonical and reproducible:
string, number, and boolean values are supported. Numbers and booleans are converted to their string representations (3, true).text = config.encode({
Zulu = { Enabled = true, Count = 3 },
Alpha = { Name = 'First', Empty = '' }
})
-- Result:
-- [Alpha]
-- Empty =
-- Name = First
--
-- [Zulu]
-- Count = 3
-- Enabled = trueAn empty table encodes to an empty string, and an empty group encodes to just its heading:
config.encode({ }) -- Result: ''
config.encode({ Empty = { } }) -- Result: '[Empty]\n'Unlike decode(), the encoder is strict and raises an exception for any input that cannot be represented as valid,
round-trippable Config text:
[, ], or line breaks.# or [, and cannot contain = or line breaks.config.encode('not a table') -- error: root must be a table
config.encode({ Broken = 'value' }) -- error: group must be a table
config.encode({ Group = { ['Bad=Key'] = 'x' } }) -- error: key cannot contain '='
config.encode({ Group = { Key = 'a\nb' } }) -- error: value cannot contain line breaksBecause the encoder enforces exactly the constraints the decoder relies on, any table that encode() accepts survives a full encode → decode cycle with its group names, key names, and string values intact:
encoded = config.encode({ ['Mixed Case'] = { ['Key With Spaces'] = 'value' } })
decoded = config.decode(encoded)
-- decoded['Mixed Case']['Key With Spaces'] = 'value'Note that the reverse is not guaranteed: decode() accepts input the encoder would reject (comments, malformed lines, duplicate keys), so decode → encode normalises the data rather than reproducing the original text byte-for-byte.
The text produced by config.encode() is directly consumable by the Config class via a DATA_TEXT data feed, which is useful when you want to build configuration data in script and then hand it to code that expects a Config object:
encoded = config.encode({ Font = { Name = 'Clean', Points = 8, Scalable = true } })
cfg = obj.new('config')
check(cfg.acDataFeed(nil, DATA_TEXT, encoded, 0))
name = [_*]cfg.mtReadValue('Font', 'Name') -- 'Clean'
points = [_*]cfg.mtReadValue('Font', 'Points') -- '8'Conversely, use config.decode() when you have Config text (for instance loaded from a file) and prefer to work with it as a plain Tiri table rather than through the object API.