Custom configuration options
This commit is contained in:
parent
50abe7a88b
commit
3a191cbd37
2 changed files with 197 additions and 0 deletions
168
src/Config.bf
Normal file
168
src/Config.bf
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
29
src/Tests/Config.bf
Normal file
29
src/Tests/Config.bf
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
namespace Common.Tests;
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
static
|
||||||
|
{
|
||||||
|
[Test(Name = "Common.Config.Default")]
|
||||||
|
public static void ParseConfig()
|
||||||
|
{
|
||||||
|
Config c = scope .("""
|
||||||
|
#this is a comment
|
||||||
|
|
||||||
|
#empty lines are fine
|
||||||
|
|
||||||
|
#even empty lines with tabs
|
||||||
|
|
||||||
|
name = value
|
||||||
|
name_1 = other value
|
||||||
|
|
||||||
|
|
||||||
|
multiline = this
|
||||||
|
can be done
|
||||||
|
like this
|
||||||
|
|
||||||
|
[Section]
|
||||||
|
stuff = inside of sections
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue