From 6dca60d5eaffff611a643e8f9ecb1172ae243ded Mon Sep 17 00:00:00 2001 From: "Harry T. Vennik" Date: Mon, 17 Jul 2017 20:04:51 +0200 Subject: [PATCH 1/2] Add support for enum types --- source/ddbus/conv.d | 40 ++++++++++++++++++++++++++++++++++++++-- source/ddbus/exception.d | 26 ++++++++++++++++++++++++++ source/ddbus/util.d | 10 ++++++++++ 3 files changed, 74 insertions(+), 2 deletions(-) diff --git a/source/ddbus/conv.d b/source/ddbus/conv.d index d75425e..85c5369 100644 --- a/source/ddbus/conv.d +++ b/source/ddbus/conv.d @@ -1,7 +1,7 @@ module ddbus.conv; import ddbus.c_lib; -import ddbus.exception : TypeMismatchException; +import ddbus.exception : InvalidValueException, TypeMismatchException; import ddbus.util; import ddbus.thin; import std.exception : enforce; @@ -119,8 +119,22 @@ void buildIter(TS...)(DBusMessageIter *iter, TS args) if(allCanDBus!TS) { } } -T readIter(T)(DBusMessageIter *iter) if (canDBus!T) { +T readIter(T)(DBusMessageIter *iter) if (is(T == enum)) { + import std.algorithm.searching : canFind; + + alias OriginalType!T B; + + B value = readIter!B(iter); + enforce( + only(EnumMembers!T).canFind(value), + new InvalidValueException(value, T.stringof) + ); + return cast(T) value; +} + +T readIter(T)(DBusMessageIter *iter) if (!is(T == enum) && canDBus!T) { T ret; + static if(!isVariant!T || is(T == Variant!DBusAny)) { if(dbus_message_iter_get_arg_type(iter) == 'v') { DBusMessageIter sub; @@ -237,6 +251,7 @@ T readIter(T)(DBusMessageIter *iter) if (canDBus!T) { } else static if(basicDBus!T) { dbus_message_iter_get_basic(iter, &ret); } + dbus_message_iter_next(iter); return ret; } @@ -269,6 +284,7 @@ unittest { 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); msg.signature().assertEqual("ibsvai(diaasabv)a{ss}tv"); @@ -298,3 +314,23 @@ unittest { readIter!DBusAny(&iter).assertEqual(anyVar); readIter!(Variant!DBusAny)(&iter).assertEqual(complexVar); } + +unittest { + import dunit.toolkit; + import ddbus.thin; + + enum E : int { a, b, c } + enum F : uint { x = 1, y = 2, z = 4 } + + Message msg = Message("org.example.wow", "/wut", "org.test.iface", "meth2"); + msg.build(E.c, 4, 5u, 8u); + + DBusMessageIter iter, iter2; + dbus_message_iter_init(msg.msg, &iter); + + readIter!E(&iter).assertEqual(E.c); + readIter!E(&iter).assertThrow!InvalidValueException(); + + readIter!F(&iter).assertThrow!InvalidValueException(); + readIter!F(&iter).assertThrow!InvalidValueException(); +} diff --git a/source/ddbus/exception.d b/source/ddbus/exception.d index 7e80bc1..a5a840f 100644 --- a/source/ddbus/exception.d +++ b/source/ddbus/exception.d @@ -64,3 +64,29 @@ class TypeMismatchException : Exception { int _expectedType; int _actualType; } + +/++ + Thrown during type conversion between DBus types and D types when a value is + encountered that can not be represented in the target type. + + This exception should not normally be thrown except when dealing with D types + that have a constrained value set, such as Enums. ++/ +class InvalidValueException : Exception { + package this(Source)( + Source value, + string targetType, + string file = __FILE__, + size_t line = __LINE__, + Throwable next = null + ) { + import std.conv : to; + + static if(__traits(compiles, value.to!string)) + string valueString = value.to!string; + else + string valueString = "(unprintable)"; + + super("Value " ~ valueString ~ " cannot be represented in type " ~ targetType); + } +} diff --git a/source/ddbus/util.d b/source/ddbus/util.d index 2754063..320a55c 100644 --- a/source/ddbus/util.d +++ b/source/ddbus/util.d @@ -47,6 +47,8 @@ template basicDBus(T) { || is (T == double) || is (T == string) || is(T == bool) || is (T == ObjectPath)) { enum basicDBus = true; + } else static if(is(T B == enum)) { + enum basicDBus = basicDBus!B; } else { enum basicDBus = false; } @@ -71,6 +73,7 @@ template canDBus(T) { enum canDBus = false; } } + unittest { import dunit.toolkit; (canDBus!int).assertTrue(); @@ -106,6 +109,8 @@ string typeSig(T)() if(canDBus!T) { return "o"; } else static if(isVariant!T) { return "v"; + } else static if(is(T B == enum)) { + return typeSig!B; } 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) { @@ -169,6 +174,11 @@ unittest { typeSig!bool().assertEqual("b"); typeSig!string().assertEqual("s"); typeSig!(Variant!int)().assertEqual("v"); + // enums + enum E : byte { a, b, c } + typeSig!E().assertEqual(typeSig!byte()); + enum U : string { One = "One", Two = "Two" } + typeSig!U().assertEqual(typeSig!string()); // structs typeSig!(Tuple!(int,string,string)).assertEqual("(iss)"); typeSig!(Tuple!(int,string,Variant!int,Tuple!(int,"k",double,"x"))).assertEqual("(isv(id))"); From e2d128cceaf20367079b0c22d27d47d2db3a58f8 Mon Sep 17 00:00:00 2001 From: "Harry T. Vennik" Date: Mon, 17 Jul 2017 20:54:35 +0200 Subject: [PATCH 2/2] Added support for BitFlags --- source/ddbus/conv.d | 23 ++++++++++++++++++++++- source/ddbus/util.d | 9 +++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/source/ddbus/conv.d b/source/ddbus/conv.d index 85c5369..3b49e0e 100644 --- a/source/ddbus/conv.d +++ b/source/ddbus/conv.d @@ -132,7 +132,24 @@ T readIter(T)(DBusMessageIter *iter) if (is(T == enum)) { return cast(T) value; } -T readIter(T)(DBusMessageIter *iter) if (!is(T == enum) && canDBus!T) { +T readIter(T)(DBusMessageIter *iter) if (isInstanceOf!(BitFlags, T)) { + import std.algorithm.iteration : fold; + + alias TemplateArgsOf!T[0] E; + alias OriginalType!E B; + + B mask = only(EnumMembers!E).fold!((a, b) => a | b); + + B value = readIter!B(iter); + enforce( + !(value & ~mask), + new InvalidValueException(value, T.stringof) + ); + + return T(cast(E) value); +} + +T readIter(T)(DBusMessageIter *iter) if (!is(T == enum) && !isInstanceOf!(BitFlags, T) && canDBus!T) { T ret; static if(!isVariant!T || is(T == Variant!DBusAny)) { @@ -331,6 +348,10 @@ unittest { readIter!E(&iter).assertEqual(E.c); readIter!E(&iter).assertThrow!InvalidValueException(); + iter2 = iter; readIter!F(&iter).assertThrow!InvalidValueException(); + readIter!(BitFlags!F)(&iter2).assertEqual(BitFlags!F(F.x, F.z)); + readIter!F(&iter).assertThrow!InvalidValueException(); + readIter!(BitFlags!F)(&iter2).assertThrow!InvalidValueException(); } diff --git a/source/ddbus/util.d b/source/ddbus/util.d index 320a55c..968e89a 100644 --- a/source/ddbus/util.d +++ b/source/ddbus/util.d @@ -49,6 +49,9 @@ template basicDBus(T) { enum basicDBus = true; } else static if(is(T B == enum)) { enum basicDBus = basicDBus!B; + } else static if(isInstanceOf!(BitFlags, T)) { + alias TemplateArgsOf!T[0] E; + enum basicDBus = basicDBus!E; } else { enum basicDBus = false; } @@ -111,6 +114,9 @@ string typeSig(T)() if(canDBus!T) { return "v"; } else static if(is(T B == enum)) { return typeSig!B; + } else static if(isInstanceOf!(BitFlags, T)) { + alias TemplateArgsOf!T[0] E; + return typeSig!E; } 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) { @@ -179,6 +185,9 @@ unittest { typeSig!E().assertEqual(typeSig!byte()); enum U : string { One = "One", Two = "Two" } typeSig!U().assertEqual(typeSig!string()); + // bit flags + enum F : uint { a = 1, b = 2, c = 4 } + typeSig!(BitFlags!F)().assertEqual(typeSig!uint()); // structs typeSig!(Tuple!(int,string,string)).assertEqual("(iss)"); typeSig!(Tuple!(int,string,Variant!int,Tuple!(int,"k",double,"x"))).assertEqual("(isv(id))");