mirror of
https://github.com/mbierlee/mirage-config.git
synced 2024-11-15 04:44:01 +01:00
Add config from other config substitution
This commit is contained in:
parent
3c7918c2e2
commit
c5bdc9d41a
|
@ -224,7 +224,12 @@ private class ConfigPath {
|
||||||
/**
|
/**
|
||||||
* Used in a ConfigDictionary to enable to disable environment variable substitution.
|
* Used in a ConfigDictionary to enable to disable environment variable substitution.
|
||||||
*/
|
*/
|
||||||
alias SubstituteEnvironmentVariables = Flag!"substituteEnvironmentVariables";
|
alias SubstituteEnvironmentVariables = Flag!"SubstituteEnvironmentVariables";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used in a ConfigDictionary to enable to disable config path substitution.
|
||||||
|
*/
|
||||||
|
alias SubstituteConfigVariables = Flag!"SubstituteConfigVariables";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A ConfigDictionary contains the configuration tree and facilities to get values from that tree.
|
* A ConfigDictionary contains the configuration tree and facilities to get values from that tree.
|
||||||
|
@ -233,16 +238,20 @@ class ConfigDictionary {
|
||||||
ConfigNode rootNode;
|
ConfigNode rootNode;
|
||||||
SubstituteEnvironmentVariables substituteEnvironmentVariables = SubstituteEnvironmentVariables
|
SubstituteEnvironmentVariables substituteEnvironmentVariables = SubstituteEnvironmentVariables
|
||||||
.yes;
|
.yes;
|
||||||
|
SubstituteConfigVariables substituteConfigVariables = SubstituteConfigVariables.yes;
|
||||||
|
|
||||||
this(SubstituteEnvironmentVariables substituteEnvironmentVariables = SubstituteEnvironmentVariables
|
this(SubstituteEnvironmentVariables substituteEnvironmentVariables = SubstituteEnvironmentVariables
|
||||||
|
.yes, SubstituteConfigVariables substituteConfigVariables = SubstituteConfigVariables
|
||||||
.yes) {
|
.yes) {
|
||||||
this.substituteEnvironmentVariables = substituteEnvironmentVariables;
|
this.substituteEnvironmentVariables = substituteEnvironmentVariables;
|
||||||
|
this.substituteConfigVariables = substituteConfigVariables;
|
||||||
}
|
}
|
||||||
|
|
||||||
this(ConfigNode rootNode, SubstituteEnvironmentVariables substituteEnvironmentVariables = SubstituteEnvironmentVariables
|
this(ConfigNode rootNode, SubstituteEnvironmentVariables substituteEnvironmentVariables = SubstituteEnvironmentVariables
|
||||||
|
.yes, SubstituteConfigVariables substituteConfigVariables = SubstituteConfigVariables
|
||||||
.yes) {
|
.yes) {
|
||||||
|
this(substituteEnvironmentVariables, substituteConfigVariables);
|
||||||
this.rootNode = rootNode;
|
this.rootNode = rootNode;
|
||||||
this.substituteEnvironmentVariables = substituteEnvironmentVariables;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -268,7 +277,11 @@ class ConfigDictionary {
|
||||||
auto node = getNodeAt(path);
|
auto node = getNodeAt(path);
|
||||||
auto value = cast(ValueNode) node;
|
auto value = cast(ValueNode) node;
|
||||||
if (value) {
|
if (value) {
|
||||||
return substituteEnvironmentVariables ? substituteEnvVars(value) : value.value;
|
if (substituteEnvironmentVariables || substituteConfigVariables) {
|
||||||
|
return substituteVariables(value);
|
||||||
|
} else {
|
||||||
|
return value.value;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new ConfigReadException(
|
throw new ConfigReadException(
|
||||||
"Value expected but " ~ node.nodeType ~ " found at path: " ~ createExceptionPath(
|
"Value expected but " ~ node.nodeType ~ " found at path: " ~ createExceptionPath(
|
||||||
|
@ -409,7 +422,7 @@ class ConfigDictionary {
|
||||||
return currentNode;
|
return currentNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string substituteEnvVars(ValueNode valueNode) {
|
private string substituteVariables(ValueNode valueNode) {
|
||||||
auto value = valueNode.value;
|
auto value = valueNode.value;
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
return value;
|
return value;
|
||||||
|
@ -418,21 +431,38 @@ class ConfigDictionary {
|
||||||
auto result = "";
|
auto result = "";
|
||||||
auto isParsingEnvVar = false;
|
auto isParsingEnvVar = false;
|
||||||
auto isParsingDefault = false;
|
auto isParsingDefault = false;
|
||||||
auto envVarName = "";
|
auto varName = "";
|
||||||
auto defaultEnvVarValue = "";
|
auto defaultVarValue = "";
|
||||||
|
|
||||||
void addEnvVarToResult() {
|
void addVarValueToResult() {
|
||||||
auto envVarValue = environment.get(envVarName);
|
string[] exceptionMessageParts;
|
||||||
if (envVarValue !is null) {
|
|
||||||
result ~= envVarValue;
|
if (substituteEnvironmentVariables) {
|
||||||
} else {
|
exceptionMessageParts ~= "environment variable";
|
||||||
if (defaultEnvVarValue.length == 0) {
|
auto envVarValue = environment.get(varName);
|
||||||
throw new ConfigReadException(
|
if (envVarValue !is null) {
|
||||||
"Environment variable not found: " ~ envVarName);
|
result ~= envVarValue;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
result ~= defaultEnvVarValue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (substituteConfigVariables) {
|
||||||
|
exceptionMessageParts ~= "config path";
|
||||||
|
auto configValue = get(varName, defaultVarValue);
|
||||||
|
if (configValue.length > 0) {
|
||||||
|
result ~= configValue;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (defaultVarValue.length > 0) {
|
||||||
|
result ~= defaultVarValue;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto exceptionMessageComponents = exceptionMessageParts.join(" or ");
|
||||||
|
throw new ConfigReadException(
|
||||||
|
"No substitution found for " ~ exceptionMessageComponents ~ ": " ~ varName);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (size_t i, char c; value) {
|
foreach (size_t i, char c; value) {
|
||||||
|
@ -449,14 +479,14 @@ class ConfigDictionary {
|
||||||
if (c == '}') {
|
if (c == '}') {
|
||||||
isParsingEnvVar = false;
|
isParsingEnvVar = false;
|
||||||
isParsingDefault = false;
|
isParsingDefault = false;
|
||||||
addEnvVarToResult();
|
addVarValueToResult();
|
||||||
envVarName = "";
|
varName = "";
|
||||||
defaultEnvVarValue = "";
|
defaultVarValue = "";
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isParsingDefault) {
|
if (isParsingDefault) {
|
||||||
defaultEnvVarValue ~= c;
|
defaultVarValue ~= c;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -465,15 +495,15 @@ class ConfigDictionary {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
envVarName ~= c;
|
varName ~= c;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
result ~= c;
|
result ~= c;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (envVarName.length > 0) {
|
if (varName.length > 0) {
|
||||||
addEnvVarToResult();
|
addVarValueToResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -794,15 +824,17 @@ version (unittest) {
|
||||||
"${MIRAGE_CONFIG_NOT_SET_TEST_ENV_VAR:use default!}"),
|
"${MIRAGE_CONFIG_NOT_SET_TEST_ENV_VAR:use default!}"),
|
||||||
"megaMix": new ValueNode("${MIRAGE_CONFIG_TEST_ENV_VAR_TWO} ${MIRAGE_CONFIG_TEST_ENV_VAR} ${MIRAGE_CONFIG_NOT_SET_TEST_ENV_VAR:go}!"),
|
"megaMix": new ValueNode("${MIRAGE_CONFIG_TEST_ENV_VAR_TWO} ${MIRAGE_CONFIG_TEST_ENV_VAR} ${MIRAGE_CONFIG_NOT_SET_TEST_ENV_VAR:go}!"),
|
||||||
"typical": new ValueNode("${MIRAGE_CONFIG_TEST_HOSTNAME:localhost}:${MIRAGE_CONFIG_TEST_PORT:8080}"),
|
"typical": new ValueNode("${MIRAGE_CONFIG_TEST_HOSTNAME:localhost}:${MIRAGE_CONFIG_TEST_PORT:8080}"),
|
||||||
])
|
]),
|
||||||
|
SubstituteEnvironmentVariables.yes,
|
||||||
|
SubstituteConfigVariables.no
|
||||||
);
|
);
|
||||||
|
|
||||||
assert(config.get("withBrackets") == "is set!");
|
assert(config.get("withBrackets") == "is set!");
|
||||||
assert(config.get("withoutBrackets") == "is set!");
|
assert(config.get("withoutBrackets") == "is set!");
|
||||||
assert(config.get("withWhiteSpace") == " is set! ");
|
assert(config.get("withWhiteSpace") == " is set! ");
|
||||||
assert(config.get("alsoWithWhiteSpace") == " is set!");
|
assert(config.get("alsoWithWhiteSpace") == " is set!");
|
||||||
assertThrown!Exception(config.get("tooMuchWhiteSpace")); // Environment variable not found (whitespace is included in env name)
|
assertThrown!ConfigReadException(config.get("tooMuchWhiteSpace")); // Environment variable not found (whitespace is included in env name)
|
||||||
assertThrown!Exception(config.get("notSet")); // Environment variable not found
|
assertThrown!ConfigReadException(config.get("notSet")); // Environment variable not found
|
||||||
assert(config.get("withDefault") == "use default!");
|
assert(config.get("withDefault") == "use default!");
|
||||||
assert(config.get("withDefaultAndBrackets") == "use default!");
|
assert(config.get("withDefaultAndBrackets") == "use default!");
|
||||||
assert(config.get("megaMix") == "is ready! is set! go!");
|
assert(config.get("megaMix") == "is ready! is set! go!");
|
||||||
|
@ -829,7 +861,8 @@ version (unittest) {
|
||||||
"megaMix": new ValueNode("${MIRAGE_CONFIG_TEST_ENV_VAR_TWO} ${MIRAGE_CONFIG_TEST_ENV_VAR} ${MIRAGE_CONFIG_NOT_SET_TEST_ENV_VAR:go}!"),
|
"megaMix": new ValueNode("${MIRAGE_CONFIG_TEST_ENV_VAR_TWO} ${MIRAGE_CONFIG_TEST_ENV_VAR} ${MIRAGE_CONFIG_NOT_SET_TEST_ENV_VAR:go}!"),
|
||||||
"typical": new ValueNode("${MIRAGE_CONFIG_TEST_HOSTNAME:localhost}:${MIRAGE_CONFIG_TEST_PORT:8080}"),
|
"typical": new ValueNode("${MIRAGE_CONFIG_TEST_HOSTNAME:localhost}:${MIRAGE_CONFIG_TEST_PORT:8080}"),
|
||||||
]),
|
]),
|
||||||
SubstituteEnvironmentVariables.no
|
SubstituteEnvironmentVariables.no,
|
||||||
|
SubstituteConfigVariables.no
|
||||||
);
|
);
|
||||||
|
|
||||||
assert(config.get("withBrackets") == "${MIRAGE_CONFIG_TEST_ENV_VAR}");
|
assert(config.get("withBrackets") == "${MIRAGE_CONFIG_TEST_ENV_VAR}");
|
||||||
|
@ -853,5 +886,58 @@ version (unittest) {
|
||||||
assert(config.get!int("do.re.mi.fa.so", 42) == 42);
|
assert(config.get!int("do.re.mi.fa.so", 42) == 42);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@("Substitute values from other config paths")
|
||||||
|
unittest {
|
||||||
|
auto config = new ConfigDictionary(
|
||||||
|
new ObjectNode([
|
||||||
|
"greet": cast(ConfigNode) new ObjectNode([
|
||||||
|
"env": "Hi"
|
||||||
|
]),
|
||||||
|
"hi": cast(ConfigNode) new ValueNode("${greet.env} there!"),
|
||||||
|
"oi": cast(ConfigNode) new ValueNode("${path.does.not.exist}"),
|
||||||
|
]),
|
||||||
|
SubstituteEnvironmentVariables.no,
|
||||||
|
SubstituteConfigVariables.yes
|
||||||
|
);
|
||||||
|
|
||||||
|
assert(config.get("hi") == "Hi there!");
|
||||||
|
assertThrown!ConfigReadException(config.get("oi"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@("Do not substitute values from other config paths when disabled")
|
||||||
|
unittest {
|
||||||
|
auto config = new ConfigDictionary(
|
||||||
|
new ObjectNode([
|
||||||
|
"greet": cast(ConfigNode) new ObjectNode([
|
||||||
|
"env": "Hi"
|
||||||
|
]),
|
||||||
|
"hi": cast(ConfigNode) new ValueNode("${greet.env} there!"),
|
||||||
|
"oi": cast(ConfigNode) new ValueNode("${path.does.not.exist}"),
|
||||||
|
]),
|
||||||
|
SubstituteEnvironmentVariables.no,
|
||||||
|
SubstituteConfigVariables.no
|
||||||
|
);
|
||||||
|
|
||||||
|
assert(config.get("greet.env") == "Hi");
|
||||||
|
assert(config.get("hi") == "${greet.env} there!");
|
||||||
|
assert(config.get("oi") == "${path.does.not.exist}");
|
||||||
|
}
|
||||||
|
|
||||||
|
@("substitute values from both environment variables and config paths")
|
||||||
|
unittest {
|
||||||
|
environment["MIRAGE_CONFIG_TEST_ENV_VAR_THREE"] = "punch";
|
||||||
|
|
||||||
|
auto config = new ConfigDictionary(
|
||||||
|
new ObjectNode([
|
||||||
|
"one": new ValueNode("${MIRAGE_CONFIG_TEST_ENV_VAR_THREE}"),
|
||||||
|
"two": new ValueNode("${one}"),
|
||||||
|
]),
|
||||||
|
SubstituteEnvironmentVariables.yes,
|
||||||
|
SubstituteConfigVariables.yes
|
||||||
|
);
|
||||||
|
|
||||||
|
assert(config.get("two") == "punch");
|
||||||
|
}
|
||||||
|
|
||||||
//TODO: Test null nodes should gracefully fail
|
//TODO: Test null nodes should gracefully fail
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue