mirror of
https://github.com/mbierlee/mirage-config.git
synced 2024-11-14 20:34:00 +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)_ | |
|
| _any below_ | _any below_ | `mirage` | `loadConfig`<sup>**</sup> | _(N/A)_ | |
|
||||||
| JSON | .json | `mirage.json` | `loadJsonConfig` | `parseJsonConfig`<sup>***</sup> | `JsonConfigFactory` |
|
| JSON | .json | `mirage.json` | `loadJsonConfig` | `parseJsonConfig`<sup>***</sup> | `JsonConfigFactory` |
|
||||||
| Java | .properties | `mirage.java` | `loadJavaProperties` | `parseJavaProperties` | `JavaPropertiesFactory` |
|
| 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> _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._
|
<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
|
# TODO
|
||||||
|
|
||||||
* Add tutorial
|
- Add tutorial
|
||||||
* Config loading
|
- Config loading
|
||||||
* Config parsing
|
- Config parsing
|
||||||
* Config manip
|
- Config manip
|
||||||
* Env and config var substitution
|
- Env and config var substitution
|
||||||
* Escaping
|
- Escaping
|
||||||
* Java properties
|
- Java properties
|
||||||
* Add unicode escaping
|
- Add unicode escaping
|
||||||
* Support multi-line values with backslash
|
- Support multi-line values with backslash
|
||||||
* Add escaping of key/value separator = and :
|
- 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.json : loadJsonConfig;
|
||||||
import mirage.java : loadJavaProperties;
|
import mirage.java : loadJavaProperties;
|
||||||
|
import mirage.ini : loadIniConfig;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used by the ConfigDictionary when something goes wrong when reading configuration.
|
* Used by the ConfigDictionary when something goes wrong when reading configuration.
|
||||||
|
@ -603,6 +604,10 @@ ConfigDictionary loadConfig(const string configPath) {
|
||||||
return loadJavaProperties(configPath);
|
return loadJavaProperties(configPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (extension == ".ini") {
|
||||||
|
return loadIniConfig(configPath);
|
||||||
|
}
|
||||||
|
|
||||||
throw new ConfigCreationException(
|
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()'");
|
"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("name") == "Groot");
|
||||||
assert(javaProperties.get("age") == "8728");
|
assert(javaProperties.get("age") == "8728");
|
||||||
assert(javaProperties.get("taxNumber") == "null");
|
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")
|
@("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;
|
module mirage;
|
||||||
|
|
||||||
public import mirage.config;
|
public import mirage.config;
|
||||||
|
public import mirage.ini;
|
||||||
public import mirage.java;
|
public import mirage.java;
|
||||||
public import mirage.json;
|
public import mirage.json;
|
||||||
public import mirage.keyvalue;
|
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