mirror of
https://github.com/mbierlee/mirage-config.git
synced 2024-11-24 16:38:55 +01:00
Add INI config factory
This commit is contained in:
parent
aaa6512af4
commit
9eac0515a1
|
@ -56,6 +56,7 @@ The following file formats are currently supported:
|
|||
| _any below_ | _any below_ | `mirage` | `loadConfig`<sup>**</sup> | _(N/A)_ | |
|
||||
| JSON | .json | `mirage.json` | `loadJsonConfig` | `parseJsonConfig`<sup>***</sup> | `JsonConfigFactory` |
|
||||
| Java | .properties | `mirage.java` | `loadJavaProperties` | `parseJavaProperties` | `JavaPropertiesFactory` |
|
||||
| INI | .ini | `mirage.ini` | `loadIniConfig` | `parseIniConfig` | `IniConfigFactory` |
|
||||
|
||||
<sup>\*</sup> _Any loader or parser can be imported from the `mirage` package since they are all publicly imported._
|
||||
<sup>\*\*</sup> _Loads files based on their extension. If the file does not use one of the extensions in the table, you must use a specific loader._
|
||||
|
|
24
TODO.md
24
TODO.md
|
@ -1,12 +1,16 @@
|
|||
# TODO
|
||||
|
||||
* Add tutorial
|
||||
* Config loading
|
||||
* Config parsing
|
||||
* Config manip
|
||||
* Env and config var substitution
|
||||
* Escaping
|
||||
* Java properties
|
||||
* Add unicode escaping
|
||||
* Support multi-line values with backslash
|
||||
* Add escaping of key/value separator = and :
|
||||
- Add tutorial
|
||||
- Config loading
|
||||
- Config parsing
|
||||
- Config manip
|
||||
- Env and config var substitution
|
||||
- Escaping
|
||||
- Java properties
|
||||
- Add unicode escaping
|
||||
- Support multi-line values with backslash
|
||||
- Add escaping of key/value separator = and :
|
||||
- INI config
|
||||
- Case insensitive properties and sections
|
||||
- Escape characters
|
||||
- Support multi-line values with backslash
|
||||
|
|
|
@ -21,6 +21,7 @@ import std.typecons : Flag;
|
|||
|
||||
import mirage.json : loadJsonConfig;
|
||||
import mirage.java : loadJavaProperties;
|
||||
import mirage.ini : loadIniConfig;
|
||||
|
||||
/**
|
||||
* Used by the ConfigDictionary when something goes wrong when reading configuration.
|
||||
|
@ -603,6 +604,10 @@ ConfigDictionary loadConfig(const string configPath) {
|
|||
return loadJavaProperties(configPath);
|
||||
}
|
||||
|
||||
if (extension == ".ini") {
|
||||
return loadIniConfig(configPath);
|
||||
}
|
||||
|
||||
throw new ConfigCreationException(
|
||||
"File extension '" ~ extension ~ "' is not recognized as a supported config file format. Please use a specific function to load it, such as 'loadJsonConfig()'");
|
||||
}
|
||||
|
@ -853,6 +858,11 @@ version (unittest) {
|
|||
assert(javaProperties.get("name") == "Groot");
|
||||
assert(javaProperties.get("age") == "8728");
|
||||
assert(javaProperties.get("taxNumber") == "null");
|
||||
|
||||
auto iniConfig = loadConfig("testfiles/groot.ini");
|
||||
assert(iniConfig.get("groot.name") == "Groot");
|
||||
assert(iniConfig.get("groot.age") == "8728");
|
||||
assert(iniConfig.get("groot.taxNumber") == "null");
|
||||
}
|
||||
|
||||
@("Whitespace is preserved in values")
|
||||
|
|
127
source/mirage/ini.d
Normal file
127
source/mirage/ini.d
Normal file
|
@ -0,0 +1,127 @@
|
|||
/**
|
||||
* Utilities for loading INI files.
|
||||
*
|
||||
* Authors:
|
||||
* Mike Bierlee, m.bierlee@lostmoment.com
|
||||
* Copyright: 2022 Mike Bierlee
|
||||
* License:
|
||||
* This software is licensed under the terms of the MIT license.
|
||||
* The full terms of the license can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
module mirage.ini;
|
||||
|
||||
import mirage.config : ConfigDictionary;
|
||||
import mirage.keyvalue : KeyValueConfigFactory, SupportHashtagComments, SupportSemicolonComments,
|
||||
SupportExclamationComments, SupportSections, NormalizeQuotedValues, SupportEqualsSeparator,
|
||||
SupportColonSeparator, SupportKeysWithoutValues;
|
||||
|
||||
/**
|
||||
* Creates configuration dictionaries from INI files.
|
||||
*
|
||||
* Format specifications:
|
||||
* https://en.wikipedia.org/wiki/INI_file#Format
|
||||
*/
|
||||
class IniConfigFactory : KeyValueConfigFactory!(
|
||||
SupportHashtagComments.yes,
|
||||
SupportSemicolonComments.yes,
|
||||
SupportExclamationComments.no,
|
||||
SupportSections.yes,
|
||||
NormalizeQuotedValues.yes,
|
||||
SupportEqualsSeparator.yes,
|
||||
SupportColonSeparator.yes,
|
||||
SupportKeysWithoutValues.no
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse configuration from the given INI config string.
|
||||
|
||||
* Params:
|
||||
* contents = Text contents of the config to be parsed.
|
||||
* Returns: The parsed configuration.
|
||||
*/
|
||||
ConfigDictionary parseIniConfig(const string contents) {
|
||||
return new IniConfigFactory().parseConfig(contents);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a INI configuration file from disk.
|
||||
*
|
||||
* Params:
|
||||
* filePath = Path to the INI configuration file.
|
||||
* Returns: The loaded configuration.
|
||||
*/
|
||||
ConfigDictionary loadIniConfig(const string filePath) {
|
||||
return new IniConfigFactory().loadFile(filePath);
|
||||
}
|
||||
|
||||
version (unittest) {
|
||||
import std.process : environment;
|
||||
|
||||
@("Parse INI config")
|
||||
unittest {
|
||||
auto config = parseIniConfig("
|
||||
globalSection = yes
|
||||
|
||||
[supersection]
|
||||
thefirst = here
|
||||
|
||||
[supersection.sub]
|
||||
sandwich=maybe tasty
|
||||
|
||||
[.way]
|
||||
advertisement? = nah ; For real, not sponsored!
|
||||
|
||||
# Although money would be cool
|
||||
[back]
|
||||
to: basics
|
||||
much = \"very much whitespace\"
|
||||
many = 'very many whitespace'
|
||||
");
|
||||
|
||||
assert(config.get("globalSection") == "yes");
|
||||
assert(config.get("supersection.thefirst") == "here");
|
||||
assert(config.get("supersection.sub.sandwich") == "maybe tasty");
|
||||
assert(config.get("supersection.sub.way.advertisement?") == "nah");
|
||||
assert(config.get("back.much") == "very much whitespace");
|
||||
assert(config.get("back.many") == "very many whitespace");
|
||||
}
|
||||
|
||||
@("Load INI file")
|
||||
unittest {
|
||||
auto config = loadIniConfig("testfiles/fuzzy.ini");
|
||||
|
||||
assert(config.get("globalSection") == "yes");
|
||||
assert(config.get("supersection.thefirst") == "here");
|
||||
assert(config.get("supersection.sub.sandwich") == "maybe tasty");
|
||||
assert(config.get("supersection.sub.way.advertisement?") == "nah");
|
||||
assert(config.get("back.much") == "very much whitespace");
|
||||
assert(config.get("back.many") == "very many whitespace");
|
||||
}
|
||||
|
||||
@("Substitute env vars")
|
||||
unittest {
|
||||
environment["MIRAGE_TEST_INI_VAR"] = "I am ini";
|
||||
auto config = parseIniConfig("
|
||||
[app]
|
||||
startInfo = ${MIRAGE_TEST_INI_VAR}
|
||||
");
|
||||
|
||||
assert(config.get("app.startInfo") == "I am ini");
|
||||
}
|
||||
|
||||
@("Use value from other key")
|
||||
unittest {
|
||||
auto config = parseIniConfig("
|
||||
[app]
|
||||
startInfo = \"Let's get started!\"
|
||||
|
||||
[logger]
|
||||
startInfo = ${app.startInfo}
|
||||
");
|
||||
|
||||
assert(config.get("app.startInfo") == "Let's get started!");
|
||||
assert(config.get("logger.startInfo") == "Let's get started!");
|
||||
}
|
||||
}
|
|
@ -10,6 +10,7 @@
|
|||
module mirage;
|
||||
|
||||
public import mirage.config;
|
||||
public import mirage.ini;
|
||||
public import mirage.java;
|
||||
public import mirage.json;
|
||||
public import mirage.keyvalue;
|
16
testfiles/fuzzy.ini
Normal file
16
testfiles/fuzzy.ini
Normal file
|
@ -0,0 +1,16 @@
|
|||
globalSection = yes
|
||||
|
||||
[supersection]
|
||||
thefirst = here
|
||||
|
||||
[supersection.sub]
|
||||
sandwich=maybe tasty
|
||||
|
||||
[.way]
|
||||
advertisement? = nah ; For real, not sponsored!
|
||||
|
||||
# Although money would be cool
|
||||
[back]
|
||||
to: basics
|
||||
much = "very much whitespace"
|
||||
many = 'very many whitespace'
|
4
testfiles/groot.ini
Normal file
4
testfiles/groot.ini
Normal file
|
@ -0,0 +1,4 @@
|
|||
[groot]
|
||||
name=Groot
|
||||
age=8728
|
||||
taxNumber=null
|
Loading…
Reference in a new issue