## What is this? This is a simplified version of the resting [configurator-ng](https://github.com/lpsmith/configurator-ng), aimed particularly to offer users of configurator-ng such as [PostgREST](postgrest.org) an easy path to migrate to a package that compiles with modern GHC versions and that continues to read existing configuration files. ## Changes configurator-pg skips some of configurator-ng's features, and changes the API in other places: * No configuration file reloading. * Simplified parsing API: - There is no type-class based value parsing; you need to supply explicit value parsers. - There's only `load` to read and evaluate a configuration file, and `runParser` to extract configuration values. * Simpler error handling. * Simplified handling of configuration subsets. There's `subassocs` and the unit tests pass, but the author didn't attempt to understand the original implementation fully. ## Credits The original configurator-ng is due to MailRank, Inc., Bryan O'Sullivan and Leon P Smith. The low-level parser (Data.Configurator.Syntax) is mostly unchanged, evaluation (Data.Configurator.Load) is also close to the original. The high-level parser (Data.Configurator.Parser) is original. ## File format In short, the file format supports: * A simple but flexible configuration language that supports several of the most commonly needed types of data, along with interpolation of strings from the configuration or the system environment (e.g. `$(HOME)`). * An `import` directive allows the configuration of a complex application to be split across several smaller files, or common configuration data to be shared across several applications. The format is more fully documented in the packages [configurator](https://hackage.haskell.org/package/configurator) and [configurator-ng](https://hackage.haskell.org/package/configurator-ng). Here's an example: ``` # listen address hostname = "localhost" port = 8000 logdir = "$(HOME)/logs" logfile = "$(logdir)/log.txt" loglevels = [1, 4, 5] users { alice = "alice@example.com" bob = "bob@example.com" } # passwords.txt might contain # alice = "12345" # bob = "sesame" passwords { import "secrets/passwords.txt" } ``` ## Usage The following code can be used to parse the example above. ``` import Data.Configurator data Settings = Settings { hostname :: Text , port :: Int , logfile :: Maybe FilePath , loglevels :: Maybe [Int] , users :: [(Text, Text)] , passwords :: [(Text, Text)] } settingsParser :: Parser Config Settings settingsParser = Settings <$> required "hostname" string <*> (Maybe.withDefault 1234 <$> optional "port" int) <*> optional "logfile" (pack <$> string) <*> optional "loglevels" (list int) <*> subassocs "users" string <*> subassocs "passwords" string loadSettings :: IO Settings loadSettings = do cfg <- load "settings.cfg" case runParser settingsParser cfg of Left err -> die $ "reading config: " <> err Right settings -> return settings ``` Though note that for no apparent reason, `subassocs` returns the full key, whence the parsed list of users will be ``` [ ("users.alice", "alice@example.com") , ("users.bob", "bob@example.com") ] ```