From 73122deb8cbfebe8d90c67bb1c3905ed3e5e7c95 Mon Sep 17 00:00:00 2001 From: WebFreak001 Date: Sun, 23 Apr 2017 15:19:15 +0200 Subject: [PATCH 1/4] Support for dynamic variants (fix #9) --- Readme.md | 5 +- source/ddbus/conv.d | 104 ++++++++++++++++- source/ddbus/router.d | 9 +- source/ddbus/simple.d | 4 +- source/ddbus/thin.d | 262 ++++++++++++++++++++++++++++++++++++++++++ source/ddbus/util.d | 4 +- 6 files changed, 378 insertions(+), 10 deletions(-) diff --git a/Readme.md b/Readme.md index bc7c86e..a79db36 100644 --- a/Readme.md +++ b/Readme.md @@ -67,8 +67,9 @@ import ddbus; MessageRouter router = new MessageRouter(); // create a pattern to register a handler at a path, interface and method MessagePattern patt = MessagePattern("/root","ca.thume.test","test"); -router.setHandler!(int,int)(patt,(int par) { - writeln("Called with ", par); +router.setHandler!(int,int,Variant!DBusAny)(patt,(int par, Variant!DBusAny anyArgument) { + // anyArgument can contain any type now, it must be specified as argument using Variant!DBusAny. + writeln("Called with ", par, ", ", anyArgument); return par; }); // handle a signal diff --git a/source/ddbus/conv.d b/source/ddbus/conv.d index 0eaae72..06e79cc 100644 --- a/source/ddbus/conv.d +++ b/source/ddbus/conv.d @@ -2,6 +2,7 @@ module ddbus.conv; import ddbus.c_lib; import ddbus.util; +import ddbus.thin; import std.string; import std.typecons; import std.range; @@ -37,6 +38,48 @@ void buildIter(TS...)(DBusMessageIter *iter, TS args) if(allCanDBus!TS) { dbus_message_iter_open_container(iter, 'v', subSig, &sub); buildIter(&sub, arg.data); dbus_message_iter_close_container(iter, &sub); + } else static if(is(T == DBusAny)) { + DBusMessageIter subStore; + DBusMessageIter* sub = &subStore; + char[] sig = [cast(char) arg.type]; + if(arg.type == 'a') + sig ~= arg.signature; + else if(arg.type == 'r') + sig = arg.signature; + sig ~= '\0'; + if (!arg.explicitVariant) + sub = iter; + else + dbus_message_iter_open_container(iter, 'v', sig.ptr, sub); + if(arg.type == 's') { + immutable(char)* cStr = arg.str.toStringz(); + dbus_message_iter_append_basic(sub,'s',&cStr); + } else if(arg.type == 'b') { + dbus_bool_t longerBool = arg.boolean; // dbus bools are ints + dbus_message_iter_append_basic(sub,'b',&longerBool); + } else if(dbus_type_is_basic(arg.type)) { + dbus_message_iter_append_basic(sub,arg.type,&arg.int64); + } else if(arg.type == 'a') { + DBusMessageIter arr; + dbus_message_iter_open_container(sub, 'a', sig[1 .. $].ptr, &arr); + foreach(item; arg.array) + buildIter(&arr, item); + dbus_message_iter_close_container(sub, &arr); + } else if(arg.type == 'r') { + DBusMessageIter arr; + dbus_message_iter_open_container(sub, 'r', null, &arr); + foreach(item; arg.tuple) + buildIter(&arr, item); + dbus_message_iter_close_container(sub, &arr); + } else if(arg.type == 'e') { + DBusMessageIter entry; + dbus_message_iter_open_container(sub, 'e', null, &entry); + buildIter(&entry, arg.entry.key); + buildIter(&entry, arg.entry.value); + dbus_message_iter_close_container(sub, &entry); + } + if(arg.explicitVariant) + dbus_message_iter_close_container(iter, sub); } else static if(is(T == DictionaryEntry!(K, V), K, V)) { DBusMessageIter sub; dbus_message_iter_open_container(iter, 'e', null, &sub); @@ -56,6 +99,8 @@ T readIter(T)(DBusMessageIter *iter) if (canDBus!T) { DBusMessageIter sub; dbus_message_iter_recurse(iter, &sub); ret = readIter!T(&sub); + static if(is(T == DBusAny)) + ret.explicitVariant = true; dbus_message_iter_next(iter); return ret; } @@ -64,7 +109,7 @@ T readIter(T)(DBusMessageIter *iter) if (canDBus!T) { assert(dbus_message_iter_get_arg_type(iter) == 'r'); } else static if(is(T == DictionaryEntry!(K1, V1), K1, V1)) { assert(dbus_message_iter_get_arg_type(iter) == 'e'); - } else { + } else static if(!is(T == DBusAny)) { assert(dbus_message_iter_get_arg_type(iter) == typeCode!T()); } static if(is(T==string)) { @@ -102,6 +147,44 @@ T readIter(T)(DBusMessageIter *iter) if (canDBus!T) { auto entry = readIter!(DictionaryEntry!(KeyType!T, ValueType!T))(&sub); ret[entry.key] = entry.value; } + } else static if(is(T == DBusAny)) { + ret.type = dbus_message_iter_get_arg_type(iter); + ret.explicitVariant = false; + if(ret.type == 's') { + const(char)* cStr; + dbus_message_iter_get_basic(iter, &cStr); + ret.str = cStr.fromStringz().idup; // copy string + } else if(ret.type == 'b') { + dbus_bool_t longerBool; + dbus_message_iter_get_basic(iter, &longerBool); + ret.boolean = cast(bool)longerBool; + } else if(dbus_type_is_basic(ret.type)) { + dbus_message_iter_get_basic(iter, &ret.int64); + } else if(ret.type == 'a') { + DBusMessageIter sub; + dbus_message_iter_recurse(iter, &sub); + auto sig = dbus_message_iter_get_signature(&sub); + ret.signature = sig.fromStringz.dup; + dbus_free(sig); + while(dbus_message_iter_get_arg_type(&sub) != 0) { + ret.array ~= readIter!DBusAny(&sub); + } + } else if(ret.type == 'r') { + auto sig = dbus_message_iter_get_signature(iter); + ret.signature = sig.fromStringz.dup; + dbus_free(sig); + DBusMessageIter sub; + dbus_message_iter_recurse(iter, &sub); + while(dbus_message_iter_get_arg_type(&sub) != 0) { + ret.tuple ~= readIter!DBusAny(&sub); + } + } else if(ret.type == 'e') { + DBusMessageIter sub; + dbus_message_iter_recurse(iter, &sub); + ret.entry = new DictionaryEntry!(DBusAny, DBusAny); + ret.entry.key = readIter!DBusAny(&sub); + ret.entry.value = readIter!DBusAny(&sub); + } } else static if(basicDBus!T) { dbus_message_iter_get_basic(iter, &ret); } @@ -123,9 +206,22 @@ unittest { bool[] emptyB; string[string] map; map["hello"] = "world"; - auto args = tuple(5,true,"wow",var(5.9),[6,5],tuple(6.2,4,[["lol"]],emptyB,var([4,2])),map); + DBusAny anyVar = DBusAny(cast(ulong) 1561); + anyVar.type.assertEqual('t'); + anyVar.uint64.assertEqual(1561); + anyVar.explicitVariant.assertEqual(false); + auto tupleMember = DBusAny(tuple(Variant!int(45), Variant!ushort(5), 32, [1, 2], tuple(variant(4), 5), map)); + DBusAny complexVar = DBusAny(variant([ + "hello world": variant(DBusAny(1337)), + "array value": variant(DBusAny([42, 64])), + "tuple value": variant(tupleMember) + ])); + complexVar.type.assertEqual('a'); + complexVar.signature.assertEqual("{sv}".dup); + tupleMember.signature.assertEqual("(vviai(vi)a{ss})"); + auto args = tuple(5,true,"wow",var(5.9),[6,5],tuple(6.2,4,[["lol"]],emptyB,var([4,2])),map,anyVar,complexVar); msg.build(args.expand); - msg.signature().assertEqual("ibsvai(diaasabv)a{ss}"); + msg.signature().assertEqual("ibsvai(diaasabv)a{ss}tv"); msg.readTuple!(typeof(args))().assertEqual(args); DBusMessageIter iter; dbus_message_iter_init(msg.msg, &iter); @@ -136,4 +232,6 @@ unittest { readIter!(int[])(&iter).assertEqual([6,5]); readIter!(Tuple!(double,int,string[][],bool[],Variant!(int[])))(&iter).assertEqual(tuple(6.2,4,[["lol"]],emptyB,var([4,2]))); readIter!(string[string])(&iter).assertEqual(["hello": "world"]); + readIter!DBusAny(&iter).assertEqual(anyVar); + readIter!DBusAny(&iter).assertEqual(complexVar); } diff --git a/source/ddbus/router.d b/source/ddbus/router.d index a712334..5550d9e 100644 --- a/source/ddbus/router.d +++ b/source/ddbus/router.d @@ -203,16 +203,21 @@ unittest{ patt = MessagePattern("/root/wat","ca.thume.tester","lolwut"); router.setHandler!(int,int)(patt,(int p) {return 6;}); patt = MessagePattern("/root/foo","ca.thume.tester","lolwut"); - router.setHandler!(Tuple!(string,string,int),int)(patt,(int p) {Tuple!(string,string,int) ret; ret[0] = "a"; ret[1] = "b"; ret[2] = p; return ret;}); + router.setHandler!(Tuple!(string,string,int),int,Variant!DBusAny)(patt,(int p, Variant!DBusAny any) {Tuple!(string,string,int) ret; ret[0] = "a"; ret[1] = "b"; ret[2] = p; return ret;}); patt = MessagePattern("/troll","ca.thume.tester","wow"); router.setHandler!(void)(patt,{return;}); + static assert(!__traits(compiles, { + patt = MessagePattern("/root/bar","ca.thume.tester","lolwut"); + router.setHandler!(void, DBusAny)(patt,(DBusAny wrongUsage){return;}); + })); + // TODO: these tests rely on nondeterministic hash map ordering static string introspectResult = ` `; router.introspectXML("/root").assertEqual(introspectResult); static string introspectResult2 = ` -`; +`; router.introspectXML("/root/foo").assertEqual(introspectResult2); router.introspectXML("/").assertEndsWith(``); } diff --git a/source/ddbus/simple.d b/source/ddbus/simple.d index 2ab786b..96cd6aa 100644 --- a/source/ddbus/simple.d +++ b/source/ddbus/simple.d @@ -74,7 +74,7 @@ void registerMethods(T : Object)(MessageRouter router, string path, string iface unittest { import dunit.toolkit; class Tester { - int lol(int x, string s, string[string] map) {return 5;} + int lol(int x, string s, string[string] map, Variant!DBusAny any) {return 5;} void wat() {} @SignalMethod void signalRecv() {} @@ -91,6 +91,6 @@ unittest { patt.signal = false; router.callTable.assertHasKey(patt); auto res = router.callTable[patt]; - res.argSig.assertEqual(["i","s", "a{ss}"]); + res.argSig.assertEqual(["i","s","a{ss}","v"]); res.retSig.assertEqual(["i"]); } diff --git a/source/ddbus/thin.d b/source/ddbus/thin.d index 8f421e2..877b7d6 100644 --- a/source/ddbus/thin.d +++ b/source/ddbus/thin.d @@ -7,6 +7,10 @@ import ddbus.util; import std.string; import std.typecons; import std.exception; +import std.traits; +import std.conv; +import std.range; +import std.algorithm; class DBusException : Exception { this(DBusError *err) { @@ -26,7 +30,265 @@ T wrapErrors(T)(T delegate(DBusError *err) del) { return ret; } +/// Structure allowing typeless parameters +struct DBusAny { + /// DBus type of the value (never 'v'), see typeSig!T + int type; + /// Child signature for Arrays & Tuples + char[] signature; + /// If true, this value will get serialized as variant value, otherwise it is serialized like it wasn't in a DBusAny wrapper. + /// Same functionality as Variant!T but with dynamic types if true. + bool explicitVariant; + + union + { + /// + byte int8; + /// + short int16; + /// + ushort uint16; + /// + int int32; + /// + uint uint32; + /// + long int64; + /// + ulong uint64; + /// + double float64; + /// + string str; + /// + bool boolean; + /// + DBusAny[] array; + /// + DBusAny[] tuple; + /// + DictionaryEntry!(DBusAny, DBusAny)* entry; + } + + /// Manually creates a DBusAny object using a type, signature and implicit specifier. + this(int type, char[] signature, bool explicit) { + this.type = type; + this.signature = signature; + this.explicitVariant = explicit; + } + + /// Automatically creates a DBusAny object with fitting parameters from a D type or Variant!T. + /// Pass a `Variant!T` to make this an explicit variant. + this(T)(T value) { + static if(is(T == byte) || is(T == ubyte)) { + this(typeCode!byte, null, false); + int8 = cast(byte) value; + } else static if(is(T == short)) { + this(typeCode!short, null, false); + int16 = cast(short) value; + } else static if(is(T == ushort)) { + this(typeCode!ushort, null, false); + uint16 = cast(ushort) value; + } else static if(is(T == int)) { + this(typeCode!int, null, false); + int32 = cast(int) value; + } else static if(is(T == uint)) { + this(typeCode!uint, null, false); + uint32 = cast(uint) value; + } else static if(is(T == long)) { + this(typeCode!long, null, false); + int64 = cast(long) value; + } else static if(is(T == ulong)) { + this(typeCode!ulong, null, false); + uint64 = cast(ulong) value; + } else static if(is(T == double)) { + this(typeCode!double, null, false); + float64 = cast(double) value; + } else static if(isSomeString!T) { + this(typeCode!string, null, false); + str = value.to!string; + } else static if(is(T == bool)) { + this(typeCode!bool, null, false); + boolean = cast(bool) value; + } else static if(is(T == Variant!R, R)) { + static if(is(R == DBusAny)) { + type = value.data.type; + signature = value.data.signature; + explicitVariant = true; + if(type == 'a' || type == 'r') + array = value.data.array; + if(type == 's') + str = value.data.str; + else if(type == 'e') + entry = value.data.entry; + else + uint64 = value.data.uint64; + } else { + this(value.data); + explicitVariant = true; + } + } else static if(is(T : DictionaryEntry!(K, V), K, V)) { + this('e', null, false); + entry = new DictionaryEntry!(DBusAny, DBusAny)(); + static if(is(K == DBusAny)) + entry.key = value.key; + else + entry.key = DBusAny(value.key); + static if(is(V == DBusAny)) + entry.value = value.value; + else + entry.value = DBusAny(value.value); + } else static if(isInputRange!T) { + this.type = 'a'; + this.signature = typeSig!(ElementType!T).dup; + this.explicitVariant = false; + foreach(elem; value) + array ~= DBusAny(elem); + } else static if(isTuple!T) { + this.type = 'r'; + this.signature = ['(']; + this.explicitVariant = false; + foreach(index, R; value.Types) { + auto var = DBusAny(value[index]); + tuple ~= var; + if(var.explicitVariant) + this.signature ~= 'v'; + else { + if (var.type != 'r') + this.signature ~= cast(char) var.type; + if(var.type == 'a' || var.type == 'r') + this.signature ~= var.signature; + } + } + this.signature ~= ')'; + } else static if(isAssociativeArray!T) { + this(value.byDictionaryEntries); + } else static assert(false, T.stringof ~ " not convertible to a Variant"); + } + + /// + string toString() const { + string valueStr; + switch(type) { + case typeCode!byte: + valueStr = int8.to!string; + break; + case typeCode!short: + valueStr = int16.to!string; + break; + case typeCode!ushort: + valueStr = uint16.to!string; + break; + case typeCode!int: + valueStr = int32.to!string; + break; + case typeCode!uint: + valueStr = uint32.to!string; + break; + case typeCode!long: + valueStr = int64.to!string; + break; + case typeCode!ulong: + valueStr = uint64.to!string; + break; + case typeCode!double: + valueStr = float64.to!string; + break; + case typeCode!string: + valueStr = '"' ~ str ~ '"'; + break; + case typeCode!bool: + valueStr = boolean ? "true" : "false"; + break; + case 'a': + valueStr = '[' ~ array.map!(a => a.toString).join(", ") ~ ']'; + break; + case 'r': + valueStr = '(' ~ array.map!(a => a.toString).join(", ") ~ ')'; + break; + case 'e': + valueStr = entry.key.toString ~ ": " ~ entry.value.toString; + break; + default: + valueStr = "unknown"; + break; + } + return "DBusAny(" ~ cast(char) type + ~ ", \"" ~ signature.idup + ~ "\", " ~ (explicitVariant ? "explicit" : "implicit") + ~ ", " ~ valueStr ~ ")"; + } + + /// If the value is an array of DictionaryEntries this will return a HashMap + DBusAny[DBusAny] toAA() { + enforce(type == 'a' && signature && signature[0] == '{'); + DBusAny[DBusAny] aa; + foreach(val; array) { + enforce(val.type == 'e'); + aa[val.entry.key] = val.entry.value; + } + return aa; + } + + /// Converts a basic type or an array to the D type with type checking. Tuples can get converted to an array. + T to(T)() { + static if(isIntegral!T || isFloatingPoint!T) { + switch(type) { + case typeCode!byte: + return cast(T) int8; + case typeCode!short: + return cast(T) int16; + case typeCode!ushort: + return cast(T) uint16; + case typeCode!int: + return cast(T) int32; + case typeCode!uint: + return cast(T) uint32; + case typeCode!long: + return cast(T) int64; + case typeCode!ulong: + return cast(T) uint64; + case typeCode!double: + return cast(T) float64; + default: + throw new Exception("Can't convert type " ~ cast(char) type ~ " to " ~ T.stringof); + } + } else static if(isSomeString!T) { + if(type == 's') + return str.to!T; + else + throw new Exception("Can't convert type " ~ cast(char) type ~ " to " ~ T.stringof); + } else static if(isDynamicArray!T) { + if(type != 'a' && type != 'r') + throw new Exception("Can't convert type " ~ cast(char) type ~ " to an array"); + T ret; + foreach(elem; array) + ret ~= elem.to!(ElementType!T); + return ret; + } else static assert(false, "Can't convert variant to " ~ T.stringof); + } + + bool opEquals(ref in DBusAny b) const { + if(b.type != type || b.explicitVariant != explicitVariant) + return false; + if((type == 'a' || type == 'r') && b.signature != signature) + return false; + if(type == 'a') + return array == b.array; + else if(type == 'r') + return tuple == b.tuple; + else if(type == 's') + return str == b.str; + else if(type == 'e') + return entry == b.entry || (entry && b.entry && *entry == *b.entry); + else + return uint64 == b.uint64; + } +} + +/// Marks the data as variant on serialization struct Variant(T) { + /// T data; } diff --git a/source/ddbus/util.d b/source/ddbus/util.d index 19b3abb..6bdaa4f 100644 --- a/source/ddbus/util.d +++ b/source/ddbus/util.d @@ -52,7 +52,7 @@ template basicDBus(T) { } template canDBus(T) { - static if(basicDBus!T) { + static if(basicDBus!T || is(T == DBusAny)) { enum canDBus = true; } else static if(isVariant!T) { enum canDBus = canDBus!(VariantType!T); @@ -101,6 +101,8 @@ string typeSig(T)() if(canDBus!T) { return "s"; } else static if(isVariant!T) { return "v"; + } else static if(is(T == DBusAny)) { + static assert(false, "Cannot determine type signature of DBusAny. Change to Variant!DBusAny if a variant was desired."); } else static if(isTuple!T) { string sig = "("; foreach(i, S; T.Types) { From d8659c9eedc3b7dcedf487abade1c0abf5a80f96 Mon Sep 17 00:00:00 2001 From: WebFreak001 Date: Sun, 23 Apr 2017 15:56:43 +0200 Subject: [PATCH 2/4] Unittest for dynamic types & added to!AA, to!Tuple --- source/ddbus/thin.d | 77 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 75 insertions(+), 2 deletions(-) diff --git a/source/ddbus/thin.d b/source/ddbus/thin.d index 877b7d6..f6e64f7 100644 --- a/source/ddbus/thin.d +++ b/source/ddbus/thin.d @@ -140,6 +140,7 @@ struct DBusAny { entry.value = DBusAny(value.value); } else static if(isInputRange!T) { this.type = 'a'; + static assert(!is(ElementType!T == DBusAny), "Array must consist of the same type, use Variant!DBusAny or DBusAny(tuple(...)) instead"); this.signature = typeSig!(ElementType!T).dup; this.explicitVariant = false; foreach(elem; value) @@ -230,9 +231,18 @@ struct DBusAny { return aa; } - /// Converts a basic type or an array to the D type with type checking. Tuples can get converted to an array. + /// Converts a basic type, a tuple or an array to the D type with type checking. Tuples can get converted to an array too. T to(T)() { - static if(isIntegral!T || isFloatingPoint!T) { + static if(is(T == Variant!R, R)) { + static if(is(R == DBusAny)) { + auto v = to!R; + v.explicitVariant = false; + return Variant!R(v); + } else + return Variant!R(to!R); + } else static if(is(T == DBusAny)) { + return this; + } else static if(isIntegral!T || isFloatingPoint!T) { switch(type) { case typeCode!byte: return cast(T) int8; @@ -253,6 +263,11 @@ struct DBusAny { default: throw new Exception("Can't convert type " ~ cast(char) type ~ " to " ~ T.stringof); } + } else static if(is(T == bool)) { + if(type == 'b') + return boolean; + else + throw new Exception("Can't convert type " ~ cast(char) type ~ " to " ~ T.stringof); } else static if(isSomeString!T) { if(type == 's') return str.to!T; @@ -265,6 +280,21 @@ struct DBusAny { foreach(elem; array) ret ~= elem.to!(ElementType!T); return ret; + } else static if(isTuple!T) { + if(type != 'r') + throw new Exception("Can't convert type " ~ cast(char) type ~ " to " ~ T.stringof); + T ret; + enforce(ret.Types.length == tuple.length, "Tuple length mismatch"); + foreach(index, T; ret.Types) + ret[index] = tuple[index].to!T; + return ret; + } else static if(isAssociativeArray!T) { + if(type != 'a' || !signature || signature[0] != '{') + throw new Exception("Can't convert type " ~ cast(char) type ~ " to " ~ T.stringof); + T ret; + foreach(pair; array) + ret[pair.entry.key.to!(KeyType!T)] = pair.entry.value.to!(ValueType!T); + return ret; } else static assert(false, "Can't convert variant to " ~ T.stringof); } @@ -286,6 +316,49 @@ struct DBusAny { } } +unittest { + import dunit.toolkit; + DBusAny set(string member, T)(DBusAny v, T value) { + mixin("v." ~ member ~ " = value;"); + return v; + } + + void test(T)(T value, DBusAny b) { + assertEqual(DBusAny(value), b); + assertEqual(b.to!T, value); + b.toString(); + } + + test(cast(ubyte) 184, set!"int8"(DBusAny('y', null, false), cast(byte) 184)); + test(cast(short) 184, set!"int16"(DBusAny('n', null, false), cast(short) 184)); + test(cast(ushort) 184, set!"uint16"(DBusAny('q', null, false), cast(ushort) 184)); + test(cast(int) 184, set!"int32"(DBusAny('i', null, false), cast(int) 184)); + test(cast(uint) 184, set!"uint32"(DBusAny('u', null, false), cast(uint) 184)); + test(cast(long) 184, set!"int64"(DBusAny('x', null, false), cast(long) 184)); + test(cast(ulong) 184, set!"uint64"(DBusAny('t', null, false), cast(ulong) 184)); + test(true, set!"boolean"(DBusAny('b', null, false), true)); + + test(variant(cast(ubyte) 184), set!"int8"(DBusAny('y', null, true), cast(byte) 184)); + test(variant(cast(short) 184), set!"int16"(DBusAny('n', null, true), cast(short) 184)); + test(variant(cast(ushort) 184), set!"uint16"(DBusAny('q', null, true), cast(ushort) 184)); + test(variant(cast(int) 184), set!"int32"(DBusAny('i', null, true), cast(int) 184)); + test(variant(cast(uint) 184), set!"uint32"(DBusAny('u', null, true), cast(uint) 184)); + test(variant(cast(long) 184), set!"int64"(DBusAny('x', null, true), cast(long) 184)); + test(variant(cast(ulong) 184), set!"uint64"(DBusAny('t', null, true), cast(ulong) 184)); + test(variant(true), set!"boolean"(DBusAny('b', null, true), true)); + + test(variant(DBusAny(5)), set!"int32"(DBusAny('i', null, true), 5)); + + test([1, 2, 3], set!"array"(DBusAny('a', ['i'], false), [DBusAny(1), DBusAny(2), DBusAny(3)])); + test(variant([1, 2, 3]), set!"array"(DBusAny('a', ['i'], true), [DBusAny(1), DBusAny(2), DBusAny(3)])); + + test(tuple("a", 4, [1, 2]), set!"tuple"(DBusAny('r', "(siai)".dup, false), [DBusAny("a"), DBusAny(4), DBusAny([1, 2])])); + test(tuple("a", variant(4), variant([1, 2])), set!"tuple"(DBusAny('r', "(svv)".dup, false), [DBusAny("a"), DBusAny(variant(4)), DBusAny(variant([1, 2]))])); + + test(["a": "b"], set!"array"(DBusAny('a', "{ss}".dup, false), [DBusAny(DictionaryEntry!(DBusAny, DBusAny)(DBusAny("a"), DBusAny("b")))])); + test([variant("a"): 4], set!"array"(DBusAny('a', "{vi}".dup, false), [DBusAny(DictionaryEntry!(DBusAny, DBusAny)(DBusAny(variant("a")), DBusAny(4)))])); +} + /// Marks the data as variant on serialization struct Variant(T) { /// From d395af0f4c69106380a317e8dce16896e90729c7 Mon Sep 17 00:00:00 2001 From: WebFreak001 Date: Sun, 23 Apr 2017 16:12:52 +0200 Subject: [PATCH 3/4] Tested & fixed returning Variant!DBusAny --- source/ddbus/conv.d | 111 +++++++++++++++++++++++------------------- source/ddbus/router.d | 4 +- 2 files changed, 63 insertions(+), 52 deletions(-) diff --git a/source/ddbus/conv.d b/source/ddbus/conv.d index 06e79cc..b2e346a 100644 --- a/source/ddbus/conv.d +++ b/source/ddbus/conv.d @@ -32,54 +32,59 @@ void buildIter(TS...)(DBusMessageIter *iter, TS args) if(allCanDBus!TS) { dbus_message_iter_close_container(iter, &sub); } else static if(isAssociativeArray!T) { buildIter(iter, arg.byDictionaryEntries); + } else static if(is(T == DBusAny) || is(T == Variant!DBusAny)) { + static if(is(T == Variant!DBusAny)) { + auto val = arg.data; + val.explicitVariant = true; + } else + auto val = arg; + DBusMessageIter subStore; + DBusMessageIter* sub = &subStore; + char[] sig = [cast(char) val.type]; + if(val.type == 'a') + sig ~= val.signature; + else if(val.type == 'r') + sig = val.signature; + sig ~= '\0'; + if (!val.explicitVariant) + sub = iter; + else + dbus_message_iter_open_container(iter, 'v', sig.ptr, sub); + if(val.type == 's') { + immutable(char)* cStr = val.str.toStringz(); + dbus_message_iter_append_basic(sub,'s',&cStr); + } else if(val.type == 'b') { + dbus_bool_t longerBool = val.boolean; // dbus bools are ints + dbus_message_iter_append_basic(sub,'b',&longerBool); + } else if(dbus_type_is_basic(val.type)) { + dbus_message_iter_append_basic(sub,val.type,&val.int64); + } else if(val.type == 'a') { + DBusMessageIter arr; + dbus_message_iter_open_container(sub, 'a', sig[1 .. $].ptr, &arr); + foreach(item; val.array) + buildIter(&arr, item); + dbus_message_iter_close_container(sub, &arr); + } else if(val.type == 'r') { + DBusMessageIter arr; + dbus_message_iter_open_container(sub, 'r', null, &arr); + foreach(item; val.tuple) + buildIter(&arr, item); + dbus_message_iter_close_container(sub, &arr); + } else if(val.type == 'e') { + DBusMessageIter entry; + dbus_message_iter_open_container(sub, 'e', null, &entry); + buildIter(&entry, val.entry.key); + buildIter(&entry, val.entry.value); + dbus_message_iter_close_container(sub, &entry); + } + if(val.explicitVariant) + dbus_message_iter_close_container(iter, sub); } else static if(isVariant!T) { DBusMessageIter sub; const(char)* subSig = typeSig!(VariantType!T).toStringz(); dbus_message_iter_open_container(iter, 'v', subSig, &sub); buildIter(&sub, arg.data); dbus_message_iter_close_container(iter, &sub); - } else static if(is(T == DBusAny)) { - DBusMessageIter subStore; - DBusMessageIter* sub = &subStore; - char[] sig = [cast(char) arg.type]; - if(arg.type == 'a') - sig ~= arg.signature; - else if(arg.type == 'r') - sig = arg.signature; - sig ~= '\0'; - if (!arg.explicitVariant) - sub = iter; - else - dbus_message_iter_open_container(iter, 'v', sig.ptr, sub); - if(arg.type == 's') { - immutable(char)* cStr = arg.str.toStringz(); - dbus_message_iter_append_basic(sub,'s',&cStr); - } else if(arg.type == 'b') { - dbus_bool_t longerBool = arg.boolean; // dbus bools are ints - dbus_message_iter_append_basic(sub,'b',&longerBool); - } else if(dbus_type_is_basic(arg.type)) { - dbus_message_iter_append_basic(sub,arg.type,&arg.int64); - } else if(arg.type == 'a') { - DBusMessageIter arr; - dbus_message_iter_open_container(sub, 'a', sig[1 .. $].ptr, &arr); - foreach(item; arg.array) - buildIter(&arr, item); - dbus_message_iter_close_container(sub, &arr); - } else if(arg.type == 'r') { - DBusMessageIter arr; - dbus_message_iter_open_container(sub, 'r', null, &arr); - foreach(item; arg.tuple) - buildIter(&arr, item); - dbus_message_iter_close_container(sub, &arr); - } else if(arg.type == 'e') { - DBusMessageIter entry; - dbus_message_iter_open_container(sub, 'e', null, &entry); - buildIter(&entry, arg.entry.key); - buildIter(&entry, arg.entry.value); - dbus_message_iter_close_container(sub, &entry); - } - if(arg.explicitVariant) - dbus_message_iter_close_container(iter, sub); } else static if(is(T == DictionaryEntry!(K, V), K, V)) { DBusMessageIter sub; dbus_message_iter_open_container(iter, 'e', null, &sub); @@ -94,13 +99,17 @@ void buildIter(TS...)(DBusMessageIter *iter, TS args) if(allCanDBus!TS) { T readIter(T)(DBusMessageIter *iter) if (canDBus!T) { T ret; - static if(!isVariant!T) { + static if(!isVariant!T || is(T == Variant!DBusAny)) { if(dbus_message_iter_get_arg_type(iter) == 'v') { DBusMessageIter sub; dbus_message_iter_recurse(iter, &sub); - ret = readIter!T(&sub); - static if(is(T == DBusAny)) - ret.explicitVariant = true; + static if(is(T == Variant!DBusAny)) { + ret = variant(readIter!DBusAny(&sub)); + } else { + ret = readIter!T(&sub); + static if(is(T == DBusAny)) + ret.explicitVariant = true; + } dbus_message_iter_next(iter); return ret; } @@ -109,7 +118,7 @@ T readIter(T)(DBusMessageIter *iter) if (canDBus!T) { assert(dbus_message_iter_get_arg_type(iter) == 'r'); } else static if(is(T == DictionaryEntry!(K1, V1), K1, V1)) { assert(dbus_message_iter_get_arg_type(iter) == 'e'); - } else static if(!is(T == DBusAny)) { + } else static if(!is(T == DBusAny) && !is(T == Variant!DBusAny)) { assert(dbus_message_iter_get_arg_type(iter) == typeCode!T()); } static if(is(T==string)) { @@ -211,13 +220,13 @@ unittest { anyVar.uint64.assertEqual(1561); anyVar.explicitVariant.assertEqual(false); auto tupleMember = DBusAny(tuple(Variant!int(45), Variant!ushort(5), 32, [1, 2], tuple(variant(4), 5), map)); - DBusAny complexVar = DBusAny(variant([ + Variant!DBusAny complexVar = variant(DBusAny([ "hello world": variant(DBusAny(1337)), "array value": variant(DBusAny([42, 64])), "tuple value": variant(tupleMember) ])); - complexVar.type.assertEqual('a'); - complexVar.signature.assertEqual("{sv}".dup); + complexVar.data.type.assertEqual('a'); + complexVar.data.signature.assertEqual("{sv}".dup); tupleMember.signature.assertEqual("(vviai(vi)a{ss})"); auto args = tuple(5,true,"wow",var(5.9),[6,5],tuple(6.2,4,[["lol"]],emptyB,var([4,2])),map,anyVar,complexVar); msg.build(args.expand); @@ -233,5 +242,5 @@ unittest { readIter!(Tuple!(double,int,string[][],bool[],Variant!(int[])))(&iter).assertEqual(tuple(6.2,4,[["lol"]],emptyB,var([4,2]))); readIter!(string[string])(&iter).assertEqual(["hello": "world"]); readIter!DBusAny(&iter).assertEqual(anyVar); - readIter!DBusAny(&iter).assertEqual(complexVar); + readIter!(Variant!DBusAny)(&iter).assertEqual(complexVar); } diff --git a/source/ddbus/router.d b/source/ddbus/router.d index 5550d9e..e999eca 100644 --- a/source/ddbus/router.d +++ b/source/ddbus/router.d @@ -202,6 +202,8 @@ unittest{ router.setHandler!(void,int,string)(patt,(int p, string p2) {}); patt = MessagePattern("/root/wat","ca.thume.tester","lolwut"); router.setHandler!(int,int)(patt,(int p) {return 6;}); + patt = MessagePattern("/root/bar","ca.thume.tester","lolwut"); + router.setHandler!(Variant!DBusAny,int)(patt,(int p) {return variant(DBusAny(p));}); patt = MessagePattern("/root/foo","ca.thume.tester","lolwut"); router.setHandler!(Tuple!(string,string,int),int,Variant!DBusAny)(patt,(int p, Variant!DBusAny any) {Tuple!(string,string,int) ret; ret[0] = "a"; ret[1] = "b"; ret[2] = p; return ret;}); patt = MessagePattern("/troll","ca.thume.tester","wow"); @@ -214,7 +216,7 @@ unittest{ // TODO: these tests rely on nondeterministic hash map ordering static string introspectResult = ` -`; +`; router.introspectXML("/root").assertEqual(introspectResult); static string introspectResult2 = ` `; From debcc8fbc623ae1b21ab4d5c905675f629d527a2 Mon Sep 17 00:00:00 2001 From: Tristan Hume Date: Sun, 23 Apr 2017 14:37:19 -0400 Subject: [PATCH 4/4] clean up things a little bit --- source/ddbus/conv.d | 17 ++++++----------- source/ddbus/thin.d | 4 +++- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/source/ddbus/conv.d b/source/ddbus/conv.d index b2e346a..015bce5 100644 --- a/source/ddbus/conv.d +++ b/source/ddbus/conv.d @@ -36,8 +36,9 @@ void buildIter(TS...)(DBusMessageIter *iter, TS args) if(allCanDBus!TS) { static if(is(T == Variant!DBusAny)) { auto val = arg.data; val.explicitVariant = true; - } else + } else { auto val = arg; + } DBusMessageIter subStore; DBusMessageIter* sub = &subStore; char[] sig = [cast(char) val.type]; @@ -51,11 +52,9 @@ void buildIter(TS...)(DBusMessageIter *iter, TS args) if(allCanDBus!TS) { else dbus_message_iter_open_container(iter, 'v', sig.ptr, sub); if(val.type == 's') { - immutable(char)* cStr = val.str.toStringz(); - dbus_message_iter_append_basic(sub,'s',&cStr); + buildIter(sub, val.str); } else if(val.type == 'b') { - dbus_bool_t longerBool = val.boolean; // dbus bools are ints - dbus_message_iter_append_basic(sub,'b',&longerBool); + buildIter(sub,val.boolean); } else if(dbus_type_is_basic(val.type)) { dbus_message_iter_append_basic(sub,val.type,&val.int64); } else if(val.type == 'a') { @@ -160,13 +159,9 @@ T readIter(T)(DBusMessageIter *iter) if (canDBus!T) { ret.type = dbus_message_iter_get_arg_type(iter); ret.explicitVariant = false; if(ret.type == 's') { - const(char)* cStr; - dbus_message_iter_get_basic(iter, &cStr); - ret.str = cStr.fromStringz().idup; // copy string + ret.str = readIter!string(iter); } else if(ret.type == 'b') { - dbus_bool_t longerBool; - dbus_message_iter_get_basic(iter, &longerBool); - ret.boolean = cast(bool)longerBool; + ret.boolean = readIter!bool(iter); } else if(dbus_type_is_basic(ret.type)) { dbus_message_iter_get_basic(iter, &ret.int64); } else if(ret.type == 'a') { diff --git a/source/ddbus/thin.d b/source/ddbus/thin.d index f6e64f7..5f9346e 100644 --- a/source/ddbus/thin.d +++ b/source/ddbus/thin.d @@ -292,8 +292,10 @@ struct DBusAny { if(type != 'a' || !signature || signature[0] != '{') throw new Exception("Can't convert type " ~ cast(char) type ~ " to " ~ T.stringof); T ret; - foreach(pair; array) + foreach(pair; array) { + enforce(pair.type == 'e'); ret[pair.entry.key.to!(KeyType!T)] = pair.entry.value.to!(ValueType!T); + } return ret; } else static assert(false, "Can't convert variant to " ~ T.stringof); }