Add support for multiline values

This commit is contained in:
Mike Bierlee 2022-10-13 21:14:25 +03:00
parent b0895cc13f
commit 86a2fa0f9f
4 changed files with 103 additions and 28 deletions

View file

@ -6,13 +6,13 @@
- Config manip
- Env and config var substitution
- Escaping
- Generic
- Base64 decoding of values
- 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
- TOML
- Add support for the standard (https://github.com/toml-lang/toml)

View file

@ -14,7 +14,7 @@ module mirage.ini;
import mirage.config : ConfigDictionary;
import mirage.keyvalue : KeyValueConfigFactory, SupportHashtagComments, SupportSemicolonComments,
SupportExclamationComments, SupportSections, NormalizeQuotedValues, SupportEqualsSeparator,
SupportColonSeparator, SupportKeysWithoutValues;
SupportColonSeparator, SupportKeysWithoutValues, SupportMultilineValues;
/**
* Creates configuration dictionaries from INI files.
@ -30,7 +30,8 @@ class IniConfigFactory : KeyValueConfigFactory!(
NormalizeQuotedValues.yes,
SupportEqualsSeparator.yes,
SupportColonSeparator.yes,
SupportKeysWithoutValues.no
SupportKeysWithoutValues.no,
SupportMultilineValues.yes
) {
}

View file

@ -14,7 +14,7 @@ module mirage.java;
import mirage.config : ConfigDictionary;
import mirage.keyvalue : KeyValueConfigFactory, SupportHashtagComments, SupportSemicolonComments,
SupportExclamationComments, SupportSections, NormalizeQuotedValues, SupportEqualsSeparator,
SupportColonSeparator, SupportKeysWithoutValues;
SupportColonSeparator, SupportKeysWithoutValues, SupportMultilineValues;
/**
* Creates configuration dictionaries from Java properties.
@ -31,7 +31,8 @@ class JavaPropertiesFactory : KeyValueConfigFactory!(
NormalizeQuotedValues.no,
SupportEqualsSeparator.yes,
SupportColonSeparator.yes,
SupportKeysWithoutValues.yes
SupportKeysWithoutValues.yes,
SupportMultilineValues.yes
) {
}

View file

@ -27,6 +27,7 @@ alias NormalizeQuotedValues = Flag!"NormalizeQuotedValues";
alias SupportEqualsSeparator = Flag!"SupportEqualsSeparator";
alias SupportColonSeparator = Flag!"SupportColonSeparator";
alias SupportKeysWithoutValues = Flag!"SupportKeysWithoutValues";
alias SupportMultilineValues = Flag!"SupportMultilineValues";
/**
* A generic reusable key/value config factory that can be configured to parse
@ -40,7 +41,8 @@ class KeyValueConfigFactory(
NormalizeQuotedValues normalizeQuotedValues = NormalizeQuotedValues.no,
SupportEqualsSeparator supportEqualsSeparator = SupportEqualsSeparator.no,
SupportColonSeparator supportColonSeparator = SupportColonSeparator.no,
SupportKeysWithoutValues supportKeysWithoutValues = SupportKeysWithoutValues.no
SupportKeysWithoutValues supportKeysWithoutValues = SupportKeysWithoutValues.no,
SupportMultilineValues supportMultilineValues = SupportMultilineValues.no
) : ConfigFactory {
/**
@ -57,6 +59,9 @@ class KeyValueConfigFactory(
auto lines = contents.lineSplitter().array;
auto properties = new ConfigDictionary();
auto section = "";
string key = null;
string valueBuffer = "";
foreach (size_t index, string line; lines) {
auto processedLine = line;
@ -75,7 +80,9 @@ class KeyValueConfigFactory(
processedLine = processedLine.strip;
if (supportSections && processedLine.startsWith('[') && processedLine.endsWith(']')) {
if (supportSections &&
key is null &&
processedLine.startsWith('[') && processedLine.endsWith(']')) {
auto parsedSection = processedLine[1 .. $ - 1];
if (parsedSection.startsWith('.')) {
section ~= parsedSection;
@ -90,6 +97,9 @@ class KeyValueConfigFactory(
continue;
}
string value;
if (key is null) {
char keyValueSplitter;
if (supportEqualsSeparator && processedLine.indexOf('=') >= 0) {
keyValueSplitter = '=';
@ -104,17 +114,28 @@ class KeyValueConfigFactory(
enforce!ConfigCreationException(supportKeysWithoutValues || parts.length == 2, "Missing value assignment (L" ~ index
.to!string ~ "): " ~ processedLine);
auto value = supportKeysWithoutValues && parts.length == 1 ? "" : parts[1].strip;
if (normalizeQuotedValues &&
value.length > 1 &&
(value.startsWith('"') || value.startsWith('\'')) &&
(value.endsWith('"') || value.endsWith('\''))) {
value = value[1 .. $ - 1];
key = [section, parts[0].strip].join('.');
value = supportKeysWithoutValues && parts.length == 1 ? "" : parts[1].strip;
} else {
value = processedLine;
}
auto key = [section, parts[0].strip].join('.');
properties.set(key, value);
if (supportMultilineValues && value.endsWith('\\')) {
valueBuffer ~= value[0 .. $ - 1];
continue;
}
auto fullValue = valueBuffer ~ value;
if (normalizeQuotedValues &&
fullValue.length > 1 &&
(fullValue.startsWith('"') || fullValue.startsWith('\'')) &&
(fullValue.endsWith('"') || fullValue.endsWith('\''))) {
fullValue = fullValue[1 .. $ - 1];
}
properties.set(key, fullValue);
key = null;
valueBuffer = "";
}
return properties;
@ -133,7 +154,8 @@ version (unittest) {
NormalizeQuotedValues.no,
SupportEqualsSeparator.yes,
SupportColonSeparator.no,
SupportKeysWithoutValues.no
SupportKeysWithoutValues.no,
SupportMultilineValues.no
) {
}
@ -325,4 +347,55 @@ version (unittest) {
assertThrown!ConfigCreationException(new KeyValueConfigFactory!()().parseConfig("a=b")); // No separator is configured
}
@("Support multiline values")
unittest {
auto config = new KeyValueConfigFactory!(
SupportHashtagComments.yes,
SupportSemicolonComments.no,
SupportExclamationComments.no,
SupportSections.yes,
NormalizeQuotedValues.yes,
SupportEqualsSeparator.yes,
SupportColonSeparator.no,
SupportKeysWithoutValues.yes,
SupportMultilineValues.yes
)().parseConfig("
sentence = the quick \\
'brown fox' \\ # comments
[jump]\\
\\
ed over \\ #are not part of the
the lazy \\
'[dog]' #value
not part of the sentence
");
assert(config.get("sentence") == "the quick 'brown fox' [jump]ed over the lazy '[dog]'");
}
@("Normalize multiline values with quotes")
unittest {
auto config = new KeyValueConfigFactory!(
SupportHashtagComments.no,
SupportSemicolonComments.no,
SupportExclamationComments.no,
SupportSections.no,
NormalizeQuotedValues.yes,
SupportEqualsSeparator.yes,
SupportColonSeparator.no,
SupportKeysWithoutValues.no,
SupportMultilineValues.yes
)().parseConfig("
doubles = \"Well then there I was \\
doing my thing.\"
singles = 'When suddenly \\
a shark bit me \\
from the sky'
");
assert(config.get("doubles") == "Well then there I was doing my thing.");
assert(config.get("singles") == "When suddenly a shark bit me from the sky");
}
}