From be76be5726c1bb13c2205fe959f28815e622ff8f Mon Sep 17 00:00:00 2001 From: WebFreak001 Date: Sun, 26 Jun 2022 02:14:04 +0200 Subject: [PATCH 1/3] add toml serializer --- src/toml/serialize.d | 221 +++++++++++++++++++++++++++++++++++++++++++ src/toml/toml.d | 2 +- 2 files changed, 222 insertions(+), 1 deletion(-) create mode 100644 src/toml/serialize.d diff --git a/src/toml/serialize.d b/src/toml/serialize.d new file mode 100644 index 0000000..9b5eabf --- /dev/null +++ b/src/toml/serialize.d @@ -0,0 +1,221 @@ +module toml.serialize; + +import toml.toml; + +import std.array; +import std.sumtype; +import std.traits; + +// UDAs: +struct tomlName { string name; } +enum tomlIgnored; +// --- + +string serializeTOML(T)(T value) +{ + auto ret = appender!string; + serializeTOML(value, ret); + return ret.data; +} + +void serializeTOML(T, Output)(T value, ref Output output) +{ + serializeTOML(value, output, "", ""); +} + +private: + +// indentation increase per level +enum oneIndentLevel = " "; + +template fieldName(alias field) +{ + enum nameUDAs = getUDAs!(field, tomlName); + static if (nameUDAs.length == 1) + enum fieldName = nameUDAs[0].name; + else static if (nameUDAs.length == 0) + enum fieldName = __traits(identifier, field); + else + static assert(false, "Field " ~ __traits(identifier, field) ~ " has multiple @tomlName UDAs"); +} + +void serializeTOML(T, Output)(T value, ref Output output, string indent, string group) + if (isPlainStruct!T) +{ + foreach (i, ref v; value.tupleof) + {{ + static if (isValueSerializable!(typeof(v)) + && getUDAs!(value.tupleof[i], tomlIgnored).length == 0 + && !isStructArray!(typeof(v))) + { + enum prefix = fieldName!(value.tupleof[i]) ~ " = "; + if (indent.length) + output.put(indent); + output.put(prefix); + serializeTOMLValue(v, output); + output.put("\n"); + } + }} + + foreach (i, ref v; value.tupleof) + {{ + static if (isValueSerializable!(typeof(v)) + && getUDAs!(value.tupleof[i], tomlIgnored).length == 0 + && isStructArray!(typeof(v))) + { + enum prefix = fieldName!(value.tupleof[i]) ~ "]]\n"; + auto deeperIndent = v.length ? indent ~ oneIndentLevel : null; + auto deepGroup = group ~ (fieldName!(value.tupleof[i]) ~ "."); + foreach (item; v) + { + output.put("\n"); + if (indent.length) + output.put(indent); + output.put("[["); + if (group.length) + output.put(group); + output.put(prefix); + serializeTOML(item, output, deeperIndent, deepGroup); + } + } + }} + + foreach (i, ref v; value.tupleof) + {{ + static if (!isValueSerializable!(typeof(v)) + && getUDAs!(value.tupleof[i], tomlIgnored).length == 0) + { + enum prefix = fieldName!(value.tupleof[i]) ~ "]\n"; + output.put("\n"); + if (indent.length) + output.put(indent); + output.put("["); + if (group.length) + output.put(group); + output.put(prefix); + serializeTOML(v, output, indent ~ oneIndentLevel, group ~ (fieldName!(value.tupleof[i]) ~ ".")); + } + }} +} + +void serializeTOML(T, Output)(T value, ref Output output, string indent, string group) + if (is(T == SumType!Types, Types...)) +{ + if (indent.length) + output.put(indent); + output.put("kind = "); + + value.match!( + (part) { + serializeTOMLValue(typeof(part).stringof, output); + output.put("\n"); + if (indent.length) + output.put(indent); + + static if (isValueSerializable!(typeof(part))) + { + static if (isStructArray!(typeof(v))) + { + auto deeperIndent = indent ~ oneIndentLevel; + auto deepGroup = group ~ "value."; + foreach (arritem; part) + { + output.put("[["); + if (group.length) + output.put(group); + output.put("value]]\n"); + serializeTOML(arritem, output, deeperIndent, deepGroup); + } + } + else + { + output.put("value = "); + serializeTOMLValue(part, output); + output.put("\n"); + } + } + else + { + output.put("["); + if (group.length) + output.put(group); + output.put("value]\n"); + serializeTOML(part, output, indent ~ oneIndentLevel, group ~ "value."); + } + } + ); +} + +// format struct arrays as expanded fields +enum isStructArray(T) = is(T == U[], U) && isPlainStruct!U; + +enum isPlainStruct(T) = is(T == struct) && !is(T == SumType!U, U...); + +enum isValueSerializable(T) = !is(T == struct); + +void serializeTOMLValue(T, Output)(T value, ref Output output) +{ + static if (__traits(compiles, { auto v = TOMLValue(value); })) + { + auto v = TOMLValue(value); + v.append(output); + } + else + static assert(false, "TODO"); +} + +unittest +{ + struct Database + { + string host; + string database; + int port; + } + + struct User + { + string name; + @tomlIgnored + string password; + @tomlName("id") + int count; + } + + struct Config + { + string token; + Database database; + int[] ports; + User[] users; + } + + Config config = { + token: "bot123", + ports: [1337, 4242, 5555], + users: [ + User("Alice", "123", 1), + User("Bob", "456", 2), + ], + database: Database("localhost", "mybot", 8080) + }; + + auto str = serializeTOML(config); + + assert(str == `token = "bot123" +ports = [1337, 4242, 5555] + +[[users]] + name = "Alice" + id = 1 + +[[users]] + name = "Bob" + id = 2 + +[database] + host = "localhost" + database = "mybot" + port = 8080 +`, str); +} diff --git a/src/toml/toml.d b/src/toml/toml.d index 5fe79ed..d120bcb 100644 --- a/src/toml/toml.d +++ b/src/toml/toml.d @@ -336,7 +336,7 @@ struct TOMLValue { } } - public inout void append(ref Appender!string appender) { + public inout void append(Output)(ref Output appender) { final switch(this._type) with(TOML_TYPE) { case STRING: appender.put(formatString(this.store.str)); From 84fa7fcb5d0f5fd59d9460a6e1ee605620e5af14 Mon Sep 17 00:00:00 2001 From: WebFreak001 Date: Sun, 26 Jun 2022 02:29:06 +0200 Subject: [PATCH 2/3] todo msg --- src/toml/serialize.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/toml/serialize.d b/src/toml/serialize.d index 9b5eabf..320243e 100644 --- a/src/toml/serialize.d +++ b/src/toml/serialize.d @@ -161,7 +161,7 @@ void serializeTOMLValue(T, Output)(T value, ref Output output) v.append(output); } else - static assert(false, "TODO"); + static assert(false, "TODO: serialize value type " ~ T.stringof ~ " not implemented"); } unittest From a15bc7c507490e0cad7a63fc1e0f7fba4d6bd054 Mon Sep 17 00:00:00 2001 From: WebFreak001 Date: Sun, 26 Jun 2022 02:47:15 +0200 Subject: [PATCH 3/3] fix sumtype, add map support for root and structs --- src/toml/serialize.d | 118 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 114 insertions(+), 4 deletions(-) diff --git a/src/toml/serialize.d b/src/toml/serialize.d index 320243e..e0c75fa 100644 --- a/src/toml/serialize.d +++ b/src/toml/serialize.d @@ -40,7 +40,7 @@ template fieldName(alias field) } void serializeTOML(T, Output)(T value, ref Output output, string indent, string group) - if (isPlainStruct!T) + if (isPlainStruct!T && !is(T == MapT[string], MapT) && !is(T == SumType!Types, Types...)) { foreach (i, ref v; value.tupleof) {{ @@ -114,7 +114,7 @@ void serializeTOML(T, Output)(T value, ref Output output, string indent, string static if (isValueSerializable!(typeof(part))) { - static if (isStructArray!(typeof(v))) + static if (isStructArray!(typeof(part))) { auto deeperIndent = indent ~ oneIndentLevel; auto deepGroup = group ~ "value."; @@ -127,12 +127,20 @@ void serializeTOML(T, Output)(T value, ref Output output, string indent, string serializeTOML(arritem, output, deeperIndent, deepGroup); } } - else + else static if (isValueSerializable!(typeof(part))) { output.put("value = "); serializeTOMLValue(part, output); output.put("\n"); } + else + { + output.put("["); + if (group.length) + output.put(group); + output.put("value]\n"); + serializeTOML(part, output, indent ~ oneIndentLevel, group ~ "value."); + } } else { @@ -146,10 +154,64 @@ void serializeTOML(T, Output)(T value, ref Output output, string indent, string ); } +void serializeTOML(T, Output)(T[string] data, ref Output output, string indent, string group) +{ + static if (isValueSerializable!T) + { + static if (isStructArray!T) + { + foreach (key, value; data) + { + if (indent.length) + output.put(indent); + output.put(key); + output.put(" = "); + serializeTOMLValue(value, output); + output.put("\n"); + } + } + else + { + auto deeperIndent = v.length ? indent ~ oneIndentLevel : null; + foreach (key, value; data) + { + foreach (arritem; value) + { + output.put("\n"); + if (indent.length) + output.put(indent); + output.put("[["); + if (group.length) + output.put(group); + output.put(key); + output.put("]]\n"); + serializeTOML(arritem, output, deeperIndent, group ~ key ~ "."); + } + } + } + } + else + { + auto deeperIndent = indent ~ oneIndentLevel; + foreach (key, value; data) + { + output.put("\n"); + if (indent.length) + output.put(indent); + output.put("["); + if (group.length) + output.put(group); + output.put(key); + output.put("]\n"); + serializeTOML(value, output, deeperIndent, group ~ key ~ "."); + } + } +} + // format struct arrays as expanded fields enum isStructArray(T) = is(T == U[], U) && isPlainStruct!U; -enum isPlainStruct(T) = is(T == struct) && !is(T == SumType!U, U...); +enum isPlainStruct(T) = is(T == struct) || is(T == V[string], V); enum isValueSerializable(T) = !is(T == struct); @@ -219,3 +281,51 @@ ports = [1337, 4242, 5555] port = 8080 `, str); } + +unittest +{ + struct Property + { + SumType!(int, string) id; + SumType!(int, string)[] attributes; + } + + Property[string] props = [ + "href": Property( + SumType!(int, string)(1), + [SumType!(int, string)(1), SumType!(int, string)("foo")], + ), + "base": Property( + SumType!(int, string)("bar"), + [SumType!(int, string)(44)], + ) + ]; + + auto str = serializeTOML(props); + + assert(str == ` +[base] + + [[base.attributes]] + kind = "int" + value = 44 + + [base.id] + kind = "string" + value = "bar" + +[href] + + [[href.attributes]] + kind = "int" + value = 1 + + [[href.attributes]] + kind = "string" + value = "foo" + + [href.id] + kind = "int" + value = 1 +`, str); +}