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