#
# Write example:
#
-# var config = new ConfigTree("config.ini")
-# config["goo"] = "goo"
-# config["foo.bar"] = "foobar"
-# config["foo.baz"] = "foobaz"
-# config.save
-# assert config.to_map.length == 3
+# var config = new ConfigTree("config.ini")
+# config["goo"] = "goo"
+# config["foo.bar"] = "foobar"
+# config["foo.baz"] = "foobaz"
+# config.save
+# assert config.to_map.length == 3
#
# Read example:
#
-# config = new ConfigTree("config.ini")
-# assert config.has_key("foo.bar")
-# assert config["foo.bar"] == "foobar"
+# config = new ConfigTree("config.ini")
+# assert config.has_key("foo.bar")
+# assert config["foo.bar"] == "foobar"
class ConfigTree
- super Streamable
+ super Writable
# The ini file used to read/store data
var ini_file: String
# Get the config value for `key`
#
- # REQUIRE: `has_key(key)`
- #
- # var config = new ConfigTree("config.ini")
- # assert config["goo"] == "goo"
- # assert config["foo.bar"] == "foobar"
- # assert config["foo.baz"] == "foobaz"
- fun [](key: String): String do
- if not has_key(key) then
- print "error: config key `{key}` not found"
- abort
- end
- var node = get_node(key).as(not null)
- if node.value == null then
- print "error: config key `{key}` has no value"
- abort
- end
- return node.value.as(not null)
+ # var config = new ConfigTree("config.ini")
+ # assert config["goo"] == "goo"
+ # assert config["foo.bar"] == "foobar"
+ # assert config["foo.baz"] == "foobaz"
+ # assert config["fail.fail"] == null
+ fun [](key: String): nullable String do
+ var node = get_node(key)
+ if node == null then return null
+ return node.value
end
# Get the config values under `key`
#
- # REQUIRE: `has_key(key)`
+ # var config = new ConfigTree("config.ini")
+ # var values = config.at("foo")
+ # assert values.has_key("bar")
+ # assert values.has_key("baz")
+ # assert not values.has_key("goo")
#
- # var config = new ConfigTree("config.ini")
- # var values = config.at("foo")
- # assert values.has_key("bar")
- # assert values.has_key("baz")
- # assert not values.has_key("goo")
- fun at(key: String): Map[String, String] do
- if not has_key(key) then
- print "error: config key `{key}` not found"
- abort
- end
+ # Return null if the key does not exists.
+ #
+ # assert config.at("fail.fail") == null
+ fun at(key: String): nullable Map[String, String] do
+ var node = get_node(key)
+ if node == null then return null
var map = new HashMap[String, String]
- var node = get_node(key).as(not null)
for k, child in node.children do
if child.value == null then continue
map[k] = child.value.to_s
# Set `value` at `key`
#
- # var config = new ConfigTree("config.ini")
- # assert config["foo.bar"] == "foobar"
- # config["foo.bar"] = "baz"
- # assert config["foo.bar"] == "baz"
+ # var config = new ConfigTree("config.ini")
+ # assert config["foo.bar"] == "foobar"
+ # config["foo.bar"] = "baz"
+ # assert config["foo.bar"] == "baz"
fun []=(key: String, value: nullable String) do
set_node(key, value)
end
# Is `key` in the config?
#
- # var config = new ConfigTree("config.ini")
- # assert config.has_key("goo")
- # assert config.has_key("foo.bar")
- # assert not config.has_key("zoo")
+ # var config = new ConfigTree("config.ini")
+ # assert config.has_key("goo")
+ # assert config.has_key("foo.bar")
+ # assert not config.has_key("zoo")
fun has_key(key: String): Bool do
- var children = roots
var parts = key.split(".").reversed
var node = get_root(parts.pop)
if node == null then return false
# Get `self` as a Map of `key`, `value`
#
- # var config = new ConfigTree("config.ini")
- # var map = config.to_map
- # assert map.has_key("goo")
- # assert map.has_key("foo.bar")
- # assert map.has_key("foo.baz")
- # assert map.length == 3
+ # var config = new ConfigTree("config.ini")
+ # var map = config.to_map
+ # assert map.has_key("goo")
+ # assert map.has_key("foo.bar")
+ # assert map.has_key("foo.baz")
+ # assert map.length == 3
fun to_map: Map[String, String] do
var map = new HashMap[String, String]
for node in leaves do
# assert config["foo.bar.baz"] == "foobarbaz"
# assert config["goo.boo.bar"] == "gooboobar"
# assert config["goo.boo.baz.bar"] == "gooboobazbar"
+ #
+ # Using the array notation
+ #
+ # str = """
+ # foo[]=a
+ # foo[]=b
+ # foo[]=c"""
+ # str.write_to_file("config4.ini")
+ # # load file
+ # config = new ConfigTree("config4.ini")
+ # print config.to_map.join(":", ",")
+ # assert config["foo.0"] == "a"
+ # assert config["foo.1"] == "b"
+ # assert config["foo.2"] == "c"
+ # assert config.at("foo").values.join(",") == "a,b,c"
fun load do
roots.clear
- var stream = new IFStream.open(ini_file)
+ var stream = new FileReader.open(ini_file)
var path: nullable String = null
+ var line_number = 0
while not stream.eof do
var line = stream.read_line
+ line_number += 1
if line.is_empty then
continue
else if line.has_prefix(";") then
path = key
set_node(path, null)
else
- var parts = line.split("=")
+ var parts = line.split_once_on("=")
+ if parts.length == 1 then
+ continue
+ end
var key = parts[0].trim
var val = parts[1].trim
- if path == null then
- set_node(key, val)
+ if path != null then key = "{path}.{key}"
+ if key.has_suffix("[]") then
+ set_array(key, val)
else
- set_node("{path}.{key}", val)
+ set_node(key,val)
end
end
end
private var roots = new Array[ConfigNode]
+ # Append `value` to array at `key`
+ private fun set_array(key: String, value: nullable String) do
+ key = key.substring(0, key.length - 2)
+ var len = 0
+ if has_key(key) then
+ len = get_node(key).children.length
+ end
+ set_node("{key}.{len.to_s}", value)
+ end
+
private fun set_node(key: String, value: nullable String) do
- var children = roots
var parts = key.split(".").reversed
var k = parts.pop
var root = get_root(k)
end
private fun get_node(key: String): nullable ConfigNode do
- var children = roots
var parts = key.split(".").reversed
var node = get_root(parts.pop)
- while not parts.is_empty do
+ while node != null and not parts.is_empty do
node = node.get_child(parts.pop)
end
return node
return null
end
end
-