README.md

# lcfg

[![Build Status][gh-actions-badge]][gh-actions]
[![LFE Versions][lfe-badge]][lfe]
[![Erlang Versions][erlang-badge]][versions]
[![Tags][github-tags-badge]][github-tags]
[![Downloads][hex-downloads]][hex-package]

*An Erlang/LFE library for reading, writing, and merging Erlang, LFE, and TOML configuration data*

[![Project Logo][logo]][logo-large]


## Contents

* [Introduction](#introduction-)
* [Usage](#usage-)
* [Development](#development-)
* [License](#license-)

## Introduction [↟](#contents)

The origins of lcfg lay in a prehistoric desire to configure LFE projects in the S-expressions of LFE syntax, as opposed to the config syntax of Erlang. That initial impetus sputtered and the finally died a mere 3-4 years after lcfg's inception.

Since then, many LFE projects have foundered on, written, rewritten, or re-invented various means of pulling configuration data from both Erlang and non-Erlang sources. Addressing some of the issues inherent in those efforts is the new use case to which lcfg now applies itself as a potential solution.

So what does this project provide for LFE and Erlang developers?

* The ability to read application configuration data (as a map) from multiple sources -- current working directory, OS-standard configuration directories, via a file passed to the Erlang VM via `-config` from release config files (e.g., `sys.config`), and from a project's `app.src` file (the `env` key).
* The ability to do all of this with a single function.
* The option of reading from all possible sources and recursively merging the config data.
* The option of reading from just one, or just one merged with the project's `env` data.

There are a few more features, but those are the broad strokes.

## Usage [↟](#contents)

Note that the examples here are taken from an LFE REPL session started with `rebar3 lfe repl`.

Show the default list of files that are searched for and read (if present) for configuration data:

``` lisp
lfe> (lcfg:show-paths 'lcfg)
"./lcfg.config"
"./lcfg.lfe"
"./lcfg.toml"
"/Users/oubiwann/Library/Application Support/lcfg/config/lcfg.config"
"/Users/oubiwann/Library/Application Support/lcfg/config/lcfg.lfe"
"/Users/oubiwann/Library/Application Support/lcfg/config/lcfg.toml"
```

The files at the top of that list have the most precedence, with the configuration data in them overriding key/values of other files, including data pulled from the application's `env`.

To view an application's `env` data:

``` lisp
lfe> (lcfg:start)
lfe> (lcfg:appenv 'lcfg)
#M(example1 #M(key1 value1 key2 value2) example2 #M(key3 value3))
```

To see the default options used for reading configuration data from various sources:

``` lisp
lfe> (lcfg:default-options)
#M(app undefined
   use-first true 
   use-env true 
   use-all false)
```

Both these options and the precedence paths listed above may be overridden in functions, allowing developers to define their own default options and orders of precedence for files and application `env` data.

Note that:
* by default, the first highest-precedence configuration file that is found will be used and merged with the application `env` data.
* by enabling `use-all` and disabling `use-first`, _all_ configuration files found will be parsed, and the subsequent maps merged in order of precedence.
* the application `env` data found in a configuration file (e.g., using the `-config` options) will be merged with -- but override the values of duplicate keys in -- the application's `app.src` config data in `env`.

More examples ... load application data using all the defaults:

``` lisp
lfe> (lcfg:load 'myapp)
```

Override default options:

``` lisp
lfe> (set opts   #m(app undefined
                    use-all true
                    use-env true
                    use-first false))
lfe> (lcfg:load 'myapp opts)
```

Override default precedence:

``` lisp
lfe> (set prec (++ (lcfg:default-precedence 'myapp) (list "~/.config/myapp.toml")))
lfe> (lcfg:load 'myapp opts prec)
```

And lastly, some functions that may come in handy when setting up configuration files on your system:

``` lisp
lfe> (lcfg:copy "priv/examples/erlang.config" 'lcfg)
#M(result #(ok 180)
   path "/Users/oubiwann/Library/Application Support/lcfg/config/lcfg.config")
```

and

``` lisp
lfe> (lcfg:move "my-config.toml" 'myapp)
#M(result #(ok 294)
   path "/Users/oubiwann/Library/Application Support/myapp/config/myapp.toml")
```

## Development [↟](#contents)

If working on this library and you want to test some Erlang functionality interactively:

``` sh
./bin/lcfg-erl -config ./priv/examples/erlang.config
```

``` erlang
1> lcfg:appenv(lcfg).
#{example1 => #{key1 => value1,key2 => value2},
  example2 => #{key3 => value3},
  example3 => #{example4 => #{key5 => value5}}}
```

Similarly, for LFE functionality:

``` sh
./bin/lcfg-lfe -config ./priv/examples/erlang.config
```

``` lisp
lfe> (lcfg:appenv 'lcfg)
#M(example1 #M(key1 value1 key2 value2) example2 #M(key3 value3)
   example3 #M(example4 #M(key5 value5)))
```

## License [↟](#contents)

Apache License, Version 2.0

Copyright © 2013-2025, Duncan McGreggor <oubiwann@gmail.com>

[//]: ---Named-Links---

[logo]: priv/images/Illustration_Ficus_carica0-small.jpg
[logo-large]: priv/images/Illustration_Ficus_carica0.jpg
[screenshot]: priv/images/screenshot.png
[org]: https://github.com/lfeutre
[github]: https://github.com/lfeutre/lcfg
[gh-actions-badge]: https://github.com/lfeutre/lcfg/workflows/ci%2Fcd/badge.svg
[gh-actions]: https://github.com/lfeutre/lcfg/actions
[lfe]: https://github.com/lfe/lfe
[lfe-badge]: https://img.shields.io/badge/lfe-2.2+-blue.svg
[erlang-badge]: https://img.shields.io/badge/erlang-23%20to%2027-blue.svg
[versions]: https://github.com/lfeutre/dirs/blob/main/rebar.config
[github-tags]: https://github.com/lfeutre/lcfg/tags
[github-tags-badge]: https://img.shields.io/github/tag/lfeutre/lcfg.svg
[github-downloads]: https://img.shields.io/github/downloads/lfeutre/lcfg/total.svg
[hex-badge]: https://img.shields.io/hexpm/v/lcfg.svg?maxAge=2592000
[hex-package]: https://hex.pm/packages/lcfg
[hex-downloads]: https://img.shields.io/hexpm/dt/lcfg.svg