Allows users to specify custom configuration options with get and set functionality Fixes: #3 Reviewed-on: #4
168 lines
3.4 KiB
Beef
168 lines
3.4 KiB
Beef
namespace Common;
|
|
|
|
using System;
|
|
using System.Collections;
|
|
|
|
/*
|
|
Simple configuration syntax
|
|
"
|
|
key = value
|
|
#comment
|
|
|
|
key = multi
|
|
line
|
|
value
|
|
|
|
[Section]
|
|
key = value
|
|
"
|
|
|
|
this(String input)
|
|
ToString(String buffer)
|
|
|
|
SetValue(section, name, value)
|
|
GetValue(section, name) : StringValue
|
|
|
|
*/
|
|
class Config
|
|
{
|
|
private (StringView, StringView) _latest = ("", "");
|
|
private Dictionary<String, Dictionary<String, String>> _data = new .()
|
|
{
|
|
(new String(""), new .()) //Unsectioned data
|
|
};
|
|
|
|
public this(StringView content)
|
|
{
|
|
for (var line in content.Split('\n'))
|
|
_ParseLine(line);
|
|
}
|
|
|
|
public ~this()
|
|
{
|
|
for (var d in _data)
|
|
DeleteDictionaryAndKeysAndValues!(d.value);
|
|
DeleteDictionaryAndKeys!(_data);
|
|
}
|
|
|
|
|
|
public void Serialize(String buffer)
|
|
{
|
|
buffer.Clear();
|
|
for (var entry in _data[""])
|
|
{
|
|
if (!entry.value.Contains('\n'))
|
|
buffer.Append(scope $"{entry.key} = {entry.value}\n");
|
|
else
|
|
{
|
|
var parts = entry.value.Split('\n');
|
|
buffer.Append(scope $"{entry.key} = {parts.GetNext().Value}\n");
|
|
for (var part in parts)
|
|
buffer.Append(scope $" {part}\n");
|
|
}
|
|
}
|
|
|
|
for (var section in _data)
|
|
{
|
|
if (section.key == "")
|
|
continue;
|
|
|
|
buffer.Append(scope $"[{section.key}]\n");
|
|
|
|
for (var entry in section.value)
|
|
{
|
|
if (!entry.value.Contains('\n'))
|
|
buffer.Append(scope $"{entry.key} = {entry.value}\n");
|
|
else
|
|
{
|
|
var parts = entry.value.Split('\n');
|
|
buffer.Append(scope $"{entry.key} = {parts.GetNext().Value}\n");
|
|
for (var part in parts)
|
|
buffer.Append(scope $" {part}\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
while (buffer.EndsWith('\n'))
|
|
buffer.RemoveFromEnd(1);
|
|
}
|
|
|
|
public Result<StringView> GetValue(StringView section, StringView name)
|
|
{
|
|
if (!_data.ContainsKeyAlt<StringView>(section))
|
|
return .Err;
|
|
|
|
var dict = _data[scope .(section)];
|
|
|
|
if (!dict.ContainsKeyAlt<StringView>(name))
|
|
return .Err;
|
|
|
|
return .Ok(dict[scope .(name)]);
|
|
}
|
|
|
|
public void SetValue(StringView section, StringView name, StringView value)
|
|
{
|
|
if (!_data.ContainsKeyAlt<StringView>(section))
|
|
_data.Add(new .(section), new .());
|
|
|
|
var dict = _data[scope .(section)];
|
|
|
|
if (!dict.ContainsKeyAlt<StringView>(name))
|
|
{
|
|
dict.Add(new .(name), new .(value));
|
|
return;
|
|
}
|
|
|
|
dict[scope .(name)].Set(value);
|
|
}
|
|
|
|
private void _ParseLine(StringView line)
|
|
{
|
|
var line;
|
|
if (line.IsEmpty || line.StartsWith('#') || (line[0] == '\t' && line.Length == 1))
|
|
return;
|
|
|
|
if (line.StartsWith('\t'))
|
|
{
|
|
if (_latest.0 == "" && _latest.1 == "")
|
|
return;
|
|
|
|
//This is safe under the assumption, that _latest did not get changed by external uses
|
|
_data.GetValue(scope .(_latest.0))
|
|
.Value.GetValue(scope .(_latest.1))
|
|
.Value.Append(scope $"\n{line.Substring(1)}");
|
|
return;
|
|
}
|
|
|
|
if (line.StartsWith('[') && !line.EndsWith(']'))
|
|
return;
|
|
|
|
else if (line.StartsWith('[') && line.EndsWith(']'))
|
|
{
|
|
if (line.Length - 2 < 0)
|
|
return;
|
|
|
|
var section = new String(line.Substring(1, line.Length - 2));
|
|
if (!_data.ContainsKeyAlt<StringView>(section))
|
|
_data.Add(section, new .());
|
|
_latest.0 = section;
|
|
_latest.1 = "";
|
|
}
|
|
|
|
if (!line.Contains(" = "))
|
|
return;
|
|
|
|
var parts = line.Split(" = ");
|
|
var name = parts.GetNext();
|
|
var text = new String(parts.Remnant);
|
|
|
|
if (_data.GetValue(scope .(_latest.0)) case .Ok(var val))
|
|
if (!val.ContainsKeyAlt<StringView>(name))
|
|
{
|
|
val.Add(new .(name), text);
|
|
_latest.1 = name;
|
|
}
|
|
}
|
|
|
|
|
|
}
|