mirror of
https://github.com/mbierlee/mirage-config.git
synced 2024-11-15 04:44:01 +01:00
Add support for getting config from objects
This commit is contained in:
parent
e3673755de
commit
a4b0e7982e
|
@ -10,7 +10,7 @@
|
||||||
module poodinis.config.dictionary;
|
module poodinis.config.dictionary;
|
||||||
|
|
||||||
import std.exception : enforce;
|
import std.exception : enforce;
|
||||||
import std.string : split, startsWith, endsWith;
|
import std.string : split, startsWith, endsWith, join;
|
||||||
import std.conv : to, ConvException;
|
import std.conv : to, ConvException;
|
||||||
|
|
||||||
class ConfigReadException : Exception {
|
class ConfigReadException : Exception {
|
||||||
|
@ -27,6 +27,7 @@ class PathParseException : Exception {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ConfigNode {
|
interface ConfigNode {
|
||||||
|
string nodeType();
|
||||||
}
|
}
|
||||||
|
|
||||||
class ValueNode : ConfigNode {
|
class ValueNode : ConfigNode {
|
||||||
|
@ -38,6 +39,10 @@ class ValueNode : ConfigNode {
|
||||||
this(string value) {
|
this(string value) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string nodeType() {
|
||||||
|
return "value";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ObjectNode : ConfigNode {
|
class ObjectNode : ConfigNode {
|
||||||
|
@ -55,6 +60,10 @@ class ObjectNode : ConfigNode {
|
||||||
children[key] = new ValueNode(value);
|
children[key] = new ValueNode(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string nodeType() {
|
||||||
|
return "object";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ArrayNode : ConfigNode {
|
class ArrayNode : ConfigNode {
|
||||||
|
@ -72,9 +81,13 @@ class ArrayNode : ConfigNode {
|
||||||
children ~= new ValueNode(value);
|
children ~= new ValueNode(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string nodeType() {
|
||||||
|
return "array";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class PathSegment {
|
interface PathSegment {
|
||||||
}
|
}
|
||||||
|
|
||||||
class ArrayPathSegment : PathSegment {
|
class ArrayPathSegment : PathSegment {
|
||||||
|
@ -95,6 +108,7 @@ class PropertyPathSegment : PathSegment {
|
||||||
|
|
||||||
class ConfigPath {
|
class ConfigPath {
|
||||||
private const string path;
|
private const string path;
|
||||||
|
private string[] previousSegments;
|
||||||
private string[] segments;
|
private string[] segments;
|
||||||
|
|
||||||
this(const string path) {
|
this(const string path) {
|
||||||
|
@ -108,7 +122,8 @@ class ConfigPath {
|
||||||
}
|
}
|
||||||
|
|
||||||
PathSegment ret(PathSegment segment) {
|
PathSegment ret(PathSegment segment) {
|
||||||
segments = segments.length > 1 ? segments[1 .. $] : [];
|
previousSegments ~= segments[0];
|
||||||
|
segments = segments[1 .. $];
|
||||||
return segment;
|
return segment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,6 +145,10 @@ class ConfigPath {
|
||||||
|
|
||||||
return ret(new PropertyPathSegment(segment));
|
return ret(new PropertyPathSegment(segment));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string getCurrentPath() {
|
||||||
|
return previousSegments.join(".");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ConfigDictionary {
|
class ConfigDictionary {
|
||||||
|
@ -151,13 +170,29 @@ class ConfigDictionary {
|
||||||
auto path = new ConfigPath(configPath);
|
auto path = new ConfigPath(configPath);
|
||||||
auto currentNode = rootNode;
|
auto currentNode = rootNode;
|
||||||
PathSegment currentPathSegment = path.getNextSegment();
|
PathSegment currentPathSegment = path.getNextSegment();
|
||||||
|
string createExceptionPath() {
|
||||||
|
return "'" ~ configPath ~ "' (at '" ~ path.getCurrentPath() ~ "')";
|
||||||
|
}
|
||||||
|
|
||||||
while (currentPathSegment !is null) {
|
while (currentPathSegment !is null) {
|
||||||
|
if (currentNode is null) {
|
||||||
|
throw new ConfigReadException(
|
||||||
|
"Path does not exist: " ~ createExceptionPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
auto valueNode = cast(ValueNode) currentNode;
|
||||||
|
if (valueNode) {
|
||||||
|
throw new ConfigReadException(
|
||||||
|
"Path does not exist: " ~ createExceptionPath());
|
||||||
|
}
|
||||||
|
|
||||||
auto arrayPath = cast(ArrayPathSegment) currentPathSegment;
|
auto arrayPath = cast(ArrayPathSegment) currentPathSegment;
|
||||||
if (arrayPath) {
|
if (arrayPath) {
|
||||||
auto arrayNode = cast(ArrayNode) currentNode;
|
auto arrayNode = cast(ArrayNode) currentNode;
|
||||||
if (arrayNode) {
|
if (arrayNode) {
|
||||||
if (arrayNode.children.length < arrayPath.index) {
|
if (arrayNode.children.length < arrayPath.index) {
|
||||||
throw new ConfigReadException("Array index out of bounds: " ~ configPath);
|
throw new ConfigReadException(
|
||||||
|
"Array index out of bounds: " ~ createExceptionPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
currentNode = arrayNode.children[arrayPath.index];
|
currentNode = arrayNode.children[arrayPath.index];
|
||||||
|
@ -171,6 +206,9 @@ class ConfigDictionary {
|
||||||
auto propertyNode = propertyPath.propertyName in objectNode.children;
|
auto propertyNode = propertyPath.propertyName in objectNode.children;
|
||||||
if (propertyNode) {
|
if (propertyNode) {
|
||||||
currentNode = *propertyNode;
|
currentNode = *propertyNode;
|
||||||
|
} else {
|
||||||
|
throw new ConfigReadException(
|
||||||
|
"Path does not exist: " ~ createExceptionPath());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -183,7 +221,7 @@ class ConfigDictionary {
|
||||||
return value.value;
|
return value.value;
|
||||||
} else {
|
} else {
|
||||||
throw new ConfigReadException(
|
throw new ConfigReadException(
|
||||||
"The configuration at the given path is not a value: " ~ configPath);
|
"Value expected but " ~ currentNode.nodeType ~ " found at path: " ~ createExceptionPath());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -256,4 +294,67 @@ version (unittest) {
|
||||||
assert(dictionary.get("noot") == "nut");
|
assert(dictionary.get("noot") == "nut");
|
||||||
assert(dictionary.get("mies") == "mies");
|
assert(dictionary.get("mies") == "mies");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@("Get value from object in object")
|
||||||
|
unittest {
|
||||||
|
auto dictionary = new ConfigDictionary();
|
||||||
|
dictionary.rootNode = new ObjectNode([
|
||||||
|
"server": new ObjectNode([
|
||||||
|
"port": "8080"
|
||||||
|
])
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert(dictionary.get("server.port") == "8080");
|
||||||
|
}
|
||||||
|
|
||||||
|
@("Get value from array in object")
|
||||||
|
unittest {
|
||||||
|
auto dictionary = new ConfigDictionary();
|
||||||
|
dictionary.rootNode = new ObjectNode([
|
||||||
|
"hostname": new ArrayNode(["google.com", "dlang.org"])
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert(dictionary.get("hostname.[1]") == "dlang.org");
|
||||||
|
}
|
||||||
|
|
||||||
|
@("Exception is thrown when array out of bounds when fetching from root")
|
||||||
|
unittest {
|
||||||
|
auto dictionary = new ConfigDictionary();
|
||||||
|
dictionary.rootNode = new ArrayNode(["google.com", "dlang.org"]);
|
||||||
|
|
||||||
|
assertThrown!ConfigReadException(dictionary.get("[5]"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@("Exception is thrown when array out of bounds when fetching from object")
|
||||||
|
unittest {
|
||||||
|
auto dictionary = new ConfigDictionary();
|
||||||
|
dictionary.rootNode = new ObjectNode([
|
||||||
|
"hostname": new ArrayNode(["google.com", "dlang.org"])
|
||||||
|
]);
|
||||||
|
|
||||||
|
assertThrown!ConfigReadException(dictionary.get("hostname.[5]"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@("Exception is thrown when path does not exist")
|
||||||
|
unittest {
|
||||||
|
auto dictionary = new ConfigDictionary();
|
||||||
|
dictionary.rootNode = new ObjectNode(
|
||||||
|
[
|
||||||
|
"hostname": new ObjectNode(["cluster": new ValueNode("")])
|
||||||
|
]);
|
||||||
|
|
||||||
|
assertThrown!ConfigReadException(dictionary.get("hostname.cluster.spacey"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@("Exception is thrown when given path terminates too early")
|
||||||
|
unittest {
|
||||||
|
auto dictionary = new ConfigDictionary();
|
||||||
|
dictionary.rootNode = new ObjectNode(
|
||||||
|
[
|
||||||
|
"hostname": new ObjectNode(["cluster": new ValueNode(null)])
|
||||||
|
]);
|
||||||
|
|
||||||
|
assertThrown!ConfigReadException(dictionary.get("hostname"));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue