.. _lua-api: .. default-domain:: lua .. meta:: :description: CFG configuration format Lua API :keywords: CFG, configuration, Lua The CFG API for Lua ------------------- The CFG reference implementation for the Lua language requires a Lua version of 5.3 or later. This documentation assumes that you will be using the `LuaRocks `__ package manager to work with packages such as this (using the name ``cfg-lib``). Installation ++++++++++++ You can install the library for use by adding `cfg_lib` to your list of dependencies in your LuaRocks ``rockspec`` file: .. code-block:: lua dependencies = { 'lua >= 5.3', 'cfg-lib >= 0.1.0' } There's a minimal example of a program that uses CFG `here `__. Exploration +++++++++++ You can use the Lua interpreter's Read-Eval-Print-Loop (REPL) functionality, invoked by running the interpreter with no arguments. You can run ``luarocks install cfg-lib`` to install the latest version of this package for your exploration. Getting Started with CFG in Lua +++++++++++++++++++++++++++++++ A configuration is accessed through the :lua:class:`Config` class. This has methods which can be passed a filename or a string which contains the text for the configuration. The text is read in, parsed and converted to an object that you can then query. A simple example: .. include:: go-test0.cfg.txt Loading a configuration ~~~~~~~~~~~~~~~~~~~~~~~ The configuration above can be loaded as shown below: .. code-block:: lua config = require('config') -- this line will be inferred in the snippets below cfg = config.Config:from_file('test0.cfg') The configuration is now available through the object referenced by ``cfg``. Access elements with keys ~~~~~~~~~~~~~~~~~~~~~~~~~ Accessing elements of the configuration with a simple key is just like using a Lua table: .. code-block:: lua print(string.format('a is "%s"', cfg.a)) print(string.format('b is "%s"', cfg.b)) which prints: .. code-block:: console a is "Hello, " b is "world!" As an alternative to using attribute notation such as ``cfg.a``, you can also use indexing notation such as ``cfg['a']``. The attribute notation can only be used if the key is a valid identifier. Access elements with paths ~~~~~~~~~~~~~~~~~~~~~~~~~~ As well as simple keys, elements can also be accessed using :term:`path` strings: .. code-block:: lua print(string.format('c.d is "%s"', cfg['c.d'])) which prints: .. code-block:: console c.d is "e" .. include:: api-snip-02.inc .. code-block:: lua print(string.format('f.g is "%s"', cfg['f.g'])) which prints: .. code-block:: console f.g is "h" Note that in the first case, you could access the value ``"e"`` using ``cfg.c.d``, but you can't access the value ``"h"``using attribute notation ``cfg.f.g`` -- you must use the indexing notation. That's because ``cfg.f`` isn't a thing in the configuration. .. include:: api-snip-03.inc Access to date/time objects ~~~~~~~~~~~~~~~~~~~~~~~~~~~ You can also get date/time objects from a configuration, by using an ISO date/time pattern in a :term:`backtick-string`: .. code-block:: lua print(string.format('Christmas morning is %s', cfg.christmas_morning)) which prints: .. code-block:: console Christmas morning is 2019-12-25 08:39:49 The returned date is a Lua table with the following keys: * ``year`` (integer) * ``month`` (integer) * ``day`` (integer) * ``hour`` (integer) * ``minute`` (integer) * ``second`` (integer or float) * ``offset_sign`` (defaults to ``"+"``) * ``offset_hour`` (integer) * ``offset_minute`` (integer) Access to environment variables ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ To access an environment variable, use a :term:`backtick-string` of the form ```$VARNAME```: .. code-block:: lua print(cfg['home'] == os.getenv('HOME')) which prints: .. code-block:: console true .. include:: api-snip-07.inc .. code-block:: lua print(string.format('foo is "%s"', cfg.foo)) which prints: .. code-block:: console foo is "bar" Access to computed values ~~~~~~~~~~~~~~~~~~~~~~~~~ .. include:: api-snip-04.inc .. code-block:: cfg-body :caption: test0a.cfg total_period : 100 header_time: 0.3 * ${total_period} steady_time: 0.5 * ${total_period} trailer_time: 0.2 * ${total_period} base_prefix: '/my/app/' log_file: ${base_prefix} + 'test.log' When this file is read in, the computed values can be accessed directly. .. code-block:: lua cfg = config.Config:from_file('test0a.cfg') for _, key in ipairs({'header_time', 'steady_time', 'trailer_time', 'log_file'}) do print(string.format('%s is %s', key, cfg[key])) end which prints: .. code-block:: console header_time is 30 steady_time is 50 trailer_time is 20 log_file is /my/app/test.log Including one configuration inside another ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ There are times when it's useful to include one configuration inside another. For example, consider the following configuration files: .. code-block:: cfg-body :caption: logging.cfg layouts: { brief: { pattern: '%d [%t] %p %c - %m%n' } }, appenders: { file: { level: 'INFO', layout: 'brief', filename: 'run/server.log', append: false, charset: 'UTF-8' }, error: { level: 'ERROR', layout: 'brief', filename: 'run/server-errors.log', append: false, charset: 'UTF-8' }, debug: { level: 'DEBUG', layout: 'brief', filename: 'run/server-debug.log', append: false, charset: 'UTF-8' } }, loggers: { mylib: { level: 'INFO' } 'mylib.detail': { level: 'DEBUG' } }, root: { handlers: ['file', 'error', 'debug'], level: 'WARNING' } .. code-block:: cfg-body :caption: redirects.cfg cookies: { url: 'http://www.allaboutcookies.org/', permanent: false }, freeotp: { url: 'https://freeotp.github.io/', permanent: false }, 'google-auth': { url: 'https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2', permanent: false } .. code-block:: cfg-body :caption: main.cfg secret: 'some random application secret', port: 8000, sitename: 'My Test Site', default_access: 'public', ignore_trailing_slashes: true, site_options: { want_ipinfo: false, show_form: true, cookie_bar: true }, debug: true, captcha_length: 4, captcha_timeout: 5, session_timeout: 7 * 24 * 60 * 60, # 7 days in seconds redirects: @'redirects.cfg', email: { sender: 'no-reply@my-domain.com', host: 'smtp.my-domain.com:587', user: 'smtp-user', password: 'smtp-pwd' } logging: @'logging.cfg' .. include:: api-snip-05.inc .. code-block:: lua cfg = config.Config:from_file('main.cfg') for _, key in ipairs({'logging.layouts.brief.pattern', 'redirects.freeotp.url', 'redirects.freeotp.permanent'}) do print(string.format('%s is %s', key, cfg[key])) end which prints: .. code-block:: console logging.layouts.brief.pattern is %d [%t] %p %c - %m%n redirects.freeotp.url is https://freeotp.github.io/ redirects.freeotp.permanent is false Avoiding unnecessary repetition ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. include:: api-snip-08.inc .. code-block:: cfg-body :caption: logging.cfg (partial) appenders: { file: { level: 'INFO', layout: 'brief', filename: 'run/server.log', append: true, charset: 'UTF-8' }, error: { level: 'ERROR', layout: 'brief', filename: 'run/server-errors.log', append: false, charset: 'UTF-8' }, debug: { level: 'DEBUG', layout: 'brief', filename: 'run/server-debug.log', append: false, charset: 'UTF-8' } }, This portion could be rewritten as: .. code-block:: cfg-body :caption: logging.cfg (partial) defs: { base_appender: { layout: 'brief', append: false, charset: 'UTF-8' } }, appenders: { file: ${defs.base_appender} + { level: 'INFO', filename: 'run/server.log', append: true, }, error: ${defs.base_appender} + { level: 'ERROR', filename: 'run/server-errors.log', }, debug: ${defs.base_appender} + { level: 'DEBUG', filename: 'run/server-debug.log', } }, .. include:: api-snip-06.inc .. code-block:: lua cfg = config.Config:from_file('main.cfg') for _, key in ipairs({'logging.appenders.file.append', 'logging.appenders.file.filename', 'logging.appenders.file.level', 'logging.appenders.debug.level', 'logging.appenders.debug.filename', 'logging.appenders.debug.append'}) do print(string.format('%s is %s', key, cfg[key])) end which prints: .. code-block:: console logging.appenders.file.append is false logging.appenders.file.filename is run/server.log logging.appenders.file.level is INFO logging.appenders.debug.level is DEBUG logging.appenders.debug.filename is run/server-debug.log logging.appenders.debug.append is false .. include:: api-snip-09.inc .. code-block:: cfg-body :caption: logging.cfg (partial) defs: { base_appender: { layout: 'brief', append: false, charset: 'UTF-8' } log_prefix: 'run/', }, appenders: { file: ${defs.base_appender} + { level: 'INFO', filename: ${defs.log_prefix} + 'server.log', append: true, }, error: ${defs.base_appender} + { level: 'ERROR', filename: ${defs.log_prefix} + 'server-errors.log', }, debug: ${defs.base_appender} + { level: 'DEBUG', filename: ${defs.log_prefix} + 'server-debug.log', } } with the same result as before. It *is* slightly more verbose than before, but the location of all files can be changed in just one place now, as opposed to three, as it was before. Classes +++++++ Classes are just Lua tables with metatables that determine their behaviour. The ``Location`` class ~~~~~~~~~~~~~~~~~~~~~~ This represents a source location and has two integer attributes, ``line`` and ``column``. The line must be positive and the column non-negative (newlines have an ending column of zero, as column 1 would have the first character of the next line). The ``ConfigError`` class ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This has an error message in the ``message`` attribute and a ``Location`` in the ``pos`` attribute indicating the position in the source of the error, where appropriate. (Some errors don't have a location, and in such cases the ``location`` attribute of the instance will be set to ``nil``.) The ``Config`` class ~~~~~~~~~~~~~~~~~~~~ This class implements access to a CFG configuration. You'll generally interface to CFG files using this class. When you pass in a string or file path to a constructor the CFG source in the string or file is parsed and converted into an internal form which can then be accessed in a manner analogous to a standard Lua table. .. index:: single: Config; Lua class .. lua:class:: Config .. cssclass:: class-members-heading Attributes .. lua:attribute:: path The path to the file from which the configuration has been read. You won't usually need to set this, unless you want to load a configuration from text that references included files. In that case, set the path to a value whose parent directory contains the included files, or specify relevant directories using :lua:attr:`~Config.include_path`. When a configuration is loaded from a file, this attribute is automatically set. .. lua:attribute:: include_path A list of directories which is searched for included sub-configurations if the parent directory of :lua:attr:`~Config.path` (or the current directory, if ``path`` isn't set) doesn't contain them. .. lua:attribute:: context A mapping of strings to objects which is used whenever an identifier is encountered when evaluating an expression in a configuration file. .. lua:attribute:: no_duplicates = true Whether keys are allowed to be duplicated in mappings. Usually, a :class:`ConfigError` is raised if a duplicate key is seen. If ``false``, then if a duplicate key is seen, its value silently replaces the value associated with the earlier appearance of the same key. .. lua:attribute:: strict_conversions = true If ``true``, a :class:`ConfigEerro` is thrown if a string can't be converted. It's intended to help catch typos in backtick-strings. .. cssclass:: class-members-heading Methods .. index:: single: Config; Config class .. lua:method:: new() :noindex: Returns a new instance. .. lua:method:: Config.from_source(source) Returns a new instance initialized with a configuration provided in ``source``. .. lua:method:: Config.from_file(filepath) Returns a new instance initialized with a configuration provided in the file specified by ``filepath``. .. lua:method:: load_file(filepath) Overwrite any configuration with a configuration provided in the file specified by ``filepath``. .. lua:method:: as_dict() Return's this instance's data as a standard Lua table. Amy expressions in the configuration are evaluated, so that the contents are standard Lua values. .. lua:method:: get(key, default_value) Retrieve a value from the instance whose key or path is represented by ``key``. If the key or path is absent from the configuration and ``default_value`` is provided, it is returned. Otherwise, a ``ConfigError`` is thrown. .. lua:method:: operator [](key) Retrieve a value from the instance whose key or path is represented by ``key``, using notation like ``cfg[key]``. If the key or path is absent from the configuration, a ``ConfigError`` is thrown. .. raw:: html