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 - Config manip
- Env and config var substitution - Env and config var substitution
- Escaping - Escaping
- Generic
- Base64 decoding of values
- Java properties - Java properties
- Add unicode escaping - Add unicode escaping
- Support multi-line values with backslash
- Add escaping of key/value separator = and : - Add escaping of key/value separator = and :
- INI config - INI config
- Case insensitive properties and sections - Case insensitive properties and sections
- Escape characters - Escape characters
- Support multi-line values with backslash
- TOML - TOML
- Add support for the standard (https://github.com/toml-lang/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.config : ConfigDictionary;
import mirage.keyvalue : KeyValueConfigFactory, SupportHashtagComments, SupportSemicolonComments, import mirage.keyvalue : KeyValueConfigFactory, SupportHashtagComments, SupportSemicolonComments,
SupportExclamationComments, SupportSections, NormalizeQuotedValues, SupportEqualsSeparator, SupportExclamationComments, SupportSections, NormalizeQuotedValues, SupportEqualsSeparator,
SupportColonSeparator, SupportKeysWithoutValues; SupportColonSeparator, SupportKeysWithoutValues, SupportMultilineValues;
/** /**
* Creates configuration dictionaries from INI files. * Creates configuration dictionaries from INI files.
@ -30,7 +30,8 @@ class IniConfigFactory : KeyValueConfigFactory!(
NormalizeQuotedValues.yes, NormalizeQuotedValues.yes,
SupportEqualsSeparator.yes, SupportEqualsSeparator.yes,
SupportColonSeparator.yes, SupportColonSeparator.yes,
SupportKeysWithoutValues.no SupportKeysWithoutValues.no,
SupportMultilineValues.yes
) { ) {
} }

View file

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

View file

@ -27,6 +27,7 @@ alias NormalizeQuotedValues = Flag!"NormalizeQuotedValues";
alias SupportEqualsSeparator = Flag!"SupportEqualsSeparator"; alias SupportEqualsSeparator = Flag!"SupportEqualsSeparator";
alias SupportColonSeparator = Flag!"SupportColonSeparator"; alias SupportColonSeparator = Flag!"SupportColonSeparator";
alias SupportKeysWithoutValues = Flag!"SupportKeysWithoutValues"; alias SupportKeysWithoutValues = Flag!"SupportKeysWithoutValues";
alias SupportMultilineValues = Flag!"SupportMultilineValues";
/** /**
* A generic reusable key/value config factory that can be configured to parse * A generic reusable key/value config factory that can be configured to parse
@ -40,7 +41,8 @@ class KeyValueConfigFactory(
NormalizeQuotedValues normalizeQuotedValues = NormalizeQuotedValues.no, NormalizeQuotedValues normalizeQuotedValues = NormalizeQuotedValues.no,
SupportEqualsSeparator supportEqualsSeparator = SupportEqualsSeparator.no, SupportEqualsSeparator supportEqualsSeparator = SupportEqualsSeparator.no,
SupportColonSeparator supportColonSeparator = SupportColonSeparator.no, SupportColonSeparator supportColonSeparator = SupportColonSeparator.no,
SupportKeysWithoutValues supportKeysWithoutValues = SupportKeysWithoutValues.no SupportKeysWithoutValues supportKeysWithoutValues = SupportKeysWithoutValues.no,
SupportMultilineValues supportMultilineValues = SupportMultilineValues.no
) : ConfigFactory { ) : ConfigFactory {
/** /**
@ -57,6 +59,9 @@ class KeyValueConfigFactory(
auto lines = contents.lineSplitter().array; auto lines = contents.lineSplitter().array;
auto properties = new ConfigDictionary(); auto properties = new ConfigDictionary();
auto section = ""; auto section = "";
string key = null;
string valueBuffer = "";
foreach (size_t index, string line; lines) { foreach (size_t index, string line; lines) {
auto processedLine = line; auto processedLine = line;
@ -75,7 +80,9 @@ class KeyValueConfigFactory(
processedLine = processedLine.strip; processedLine = processedLine.strip;
if (supportSections && processedLine.startsWith('[') && processedLine.endsWith(']')) { if (supportSections &&
key is null &&
processedLine.startsWith('[') && processedLine.endsWith(']')) {
auto parsedSection = processedLine[1 .. $ - 1]; auto parsedSection = processedLine[1 .. $ - 1];
if (parsedSection.startsWith('.')) { if (parsedSection.startsWith('.')) {
section ~= parsedSection; section ~= parsedSection;
@ -90,31 +97,45 @@ class KeyValueConfigFactory(
continue; continue;
} }
char keyValueSplitter; string value;
if (supportEqualsSeparator && processedLine.indexOf('=') >= 0) {
keyValueSplitter = '='; if (key is null) {
} else if (supportColonSeparator && processedLine.indexOf(':') >= 0) { char keyValueSplitter;
keyValueSplitter = ':'; if (supportEqualsSeparator && processedLine.indexOf('=') >= 0) {
keyValueSplitter = '=';
} else if (supportColonSeparator && processedLine.indexOf(':') >= 0) {
keyValueSplitter = ':';
}
auto parts = processedLine.split(keyValueSplitter);
enforce!ConfigCreationException(parts.length <= 2, "Line has too many equals signs and cannot be parsed (L" ~ index
.to!string ~ "): " ~ processedLine);
enforce!ConfigCreationException(supportKeysWithoutValues || parts.length == 2, "Missing value assignment (L" ~ index
.to!string ~ "): " ~ processedLine);
key = [section, parts[0].strip].join('.');
value = supportKeysWithoutValues && parts.length == 1 ? "" : parts[1].strip;
} else {
value = processedLine;
} }
auto parts = processedLine.split(keyValueSplitter); if (supportMultilineValues && value.endsWith('\\')) {
valueBuffer ~= value[0 .. $ - 1];
enforce!ConfigCreationException(parts.length <= 2, "Line has too many equals signs and cannot be parsed (L" ~ index continue;
.to!string ~ "): " ~ processedLine); }
enforce!ConfigCreationException(supportKeysWithoutValues || parts.length == 2, "Missing value assignment (L" ~ index
.to!string ~ "): " ~ processedLine);
auto value = supportKeysWithoutValues && parts.length == 1 ? "" : parts[1].strip;
auto fullValue = valueBuffer ~ value;
if (normalizeQuotedValues && if (normalizeQuotedValues &&
value.length > 1 && fullValue.length > 1 &&
(value.startsWith('"') || value.startsWith('\'')) && (fullValue.startsWith('"') || fullValue.startsWith('\'')) &&
(value.endsWith('"') || value.endsWith('\''))) { (fullValue.endsWith('"') || fullValue.endsWith('\''))) {
value = value[1 .. $ - 1]; fullValue = fullValue[1 .. $ - 1];
} }
auto key = [section, parts[0].strip].join('.'); properties.set(key, fullValue);
properties.set(key, value); key = null;
valueBuffer = "";
} }
return properties; return properties;
@ -133,7 +154,8 @@ version (unittest) {
NormalizeQuotedValues.no, NormalizeQuotedValues.no,
SupportEqualsSeparator.yes, SupportEqualsSeparator.yes,
SupportColonSeparator.no, 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 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");
}
} }