Added support for Phobos-style variants

Only variants that are limited to contain types supported by ddbus are allowed.
This commit is contained in:
Harry T. Vennik 2017-08-06 19:52:13 +02:00
parent da536be3d1
commit e6d03252d2
3 changed files with 68 additions and 10 deletions

View file

@ -10,6 +10,7 @@ import std.string;
import std.typecons; import std.typecons;
import std.range; import std.range;
import std.traits; import std.traits;
import std.variant : VariantN;
void buildIter(TS...)(DBusMessageIter *iter, TS args) if(allCanDBus!TS) { void buildIter(TS...)(DBusMessageIter *iter, TS args) if(allCanDBus!TS) {
foreach(index, arg; args) { foreach(index, arg; args) {
@ -56,6 +57,19 @@ void buildIter(TS...)(DBusMessageIter *iter, TS args) if(allCanDBus!TS) {
dbus_message_iter_close_container(&sub, &entry); dbus_message_iter_close_container(&sub, &entry);
} }
dbus_message_iter_close_container(iter, &sub); dbus_message_iter_close_container(iter, &sub);
} else static if(isInstanceOf!(VariantN, T)) {
enforce(arg.hasValue,
new InvalidValueException(arg, "dbus:" ~ cast(char) typeCode!T));
DBusMessageIter sub;
foreach(AT; T.AllowedTypes) {
if (arg.peek!AT) {
dbus_message_iter_open_container(iter, 'v', typeSig!AT.ptr, &sub);
buildIter(&sub, arg.get!AT);
dbus_message_iter_close_container(iter, &sub);
break;
}
}
} else static if(is(T == DBusAny) || is(T == Variant!DBusAny)) { } else static if(is(T == DBusAny) || is(T == Variant!DBusAny)) {
static if(is(T == Variant!DBusAny)) { static if(is(T == Variant!DBusAny)) {
auto val = arg.data; auto val = arg.data;
@ -171,10 +185,11 @@ T readIter(T)(DBusMessageIter *iter) if (isInstanceOf!(BitFlags, T)) {
} }
T readIter(T)(DBusMessageIter *iter) if (!is(T == enum) && !isInstanceOf!(BitFlags, T) && canDBus!T) { T readIter(T)(DBusMessageIter *iter) if (!is(T == enum) && !isInstanceOf!(BitFlags, T) && canDBus!T) {
auto argType = dbus_message_iter_get_arg_type(iter);
T ret; T ret;
static if(!isVariant!T || is(T == Variant!DBusAny)) { static if(!isVariant!T || is(T == Variant!DBusAny)) {
if(dbus_message_iter_get_arg_type(iter) == 'v') { if(argType == 'v') {
DBusMessageIter sub; DBusMessageIter sub;
dbus_message_iter_recurse(iter, &sub); dbus_message_iter_recurse(iter, &sub);
static if(is(T == Variant!DBusAny)) { static if(is(T == Variant!DBusAny)) {
@ -188,8 +203,12 @@ T readIter(T)(DBusMessageIter *iter) if (!is(T == enum) && !isInstanceOf!(BitFla
return ret; return ret;
} }
} }
static if(!is(T == DBusAny) && !is(T == Variant!DBusAny)) {
auto argType = dbus_message_iter_get_arg_type(iter); static if(
!is(T == DBusAny)
&& !is(T == Variant!DBusAny)
&& !isInstanceOf!(VariantN, T)
) {
enforce(argType == typeCode!T(), enforce(argType == typeCode!T(),
new TypeMismatchException(typeCode!T(), argType)); new TypeMismatchException(typeCode!T(), argType));
} }
@ -227,6 +246,25 @@ T readIter(T)(DBusMessageIter *iter) if (!is(T == enum) && !isInstanceOf!(BitFla
DBusMessageIter sub; DBusMessageIter sub;
dbus_message_iter_recurse(iter, &sub); dbus_message_iter_recurse(iter, &sub);
ret.data = readIter!(VariantType!T)(&sub); ret.data = readIter!(VariantType!T)(&sub);
} else static if(isInstanceOf!(VariantN, T)) {
scope const(char)[] argSig =
dbus_message_iter_get_signature(iter).fromStringz();
scope(exit)
dbus_free(cast(void*) argSig.ptr);
foreach(AT; T.AllowedTypes) {
// We have to compare the full signature here, not just the typecode.
// Otherwise, in case of container types, we might select the wrong one.
// We would then be calling an incorrect instance of readIter, which would
// probably throw a TypeMismatchException.
if (typeSig!AT == argSig) {
ret = readIter!AT(iter);
break;
}
}
// If no value is in ret, apparently none of the types matched.
enforce(ret.hasValue, new TypeMismatchException(typeCode!T, argType));
} else static if(isAssociativeArray!T) { } else static if(isAssociativeArray!T) {
DBusMessageIter sub; DBusMessageIter sub;
dbus_message_iter_recurse(iter, &sub); dbus_message_iter_recurse(iter, &sub);
@ -239,7 +277,7 @@ T readIter(T)(DBusMessageIter *iter) if (!is(T == enum) && !isInstanceOf!(BitFla
dbus_message_iter_next(&sub); dbus_message_iter_next(&sub);
} }
} else static if(is(T == DBusAny)) { } else static if(is(T == DBusAny)) {
ret.type = dbus_message_iter_get_arg_type(iter); ret.type = argType;
ret.explicitVariant = false; ret.explicitVariant = false;
if(ret.type == 's') { if(ret.type == 's') {
ret.str = readIter!string(iter); ret.str = readIter!string(iter);
@ -428,11 +466,16 @@ unittest {
import dunit.toolkit; import dunit.toolkit;
import ddbus.thin; import ddbus.thin;
import std.variant : Algebraic;
enum E : int { a, b, c } enum E : int { a, b, c }
enum F : uint { x = 1, y = 2, z = 4 } enum F : uint { x = 1, y = 2, z = 4 }
alias V = Algebraic!(byte, short, int, long, string);
Message msg = Message("org.example.wow", "/wut", "org.test.iface", "meth2"); Message msg = Message("org.example.wow", "/wut", "org.test.iface", "meth2");
msg.build(E.c, 4, 5u, 8u); V v1 = "hello from variant";
V v2 = cast(short) 345;
msg.build(E.c, 4, 5u, 8u, v1, v2);
DBusMessageIter iter, iter2; DBusMessageIter iter, iter2;
dbus_message_iter_init(msg.msg, &iter); dbus_message_iter_init(msg.msg, &iter);
@ -446,5 +489,8 @@ unittest {
readIter!F(&iter).assertThrow!InvalidValueException(); readIter!F(&iter).assertThrow!InvalidValueException();
readIter!(BitFlags!F)(&iter2).assertThrow!InvalidValueException(); readIter!(BitFlags!F)(&iter2).assertThrow!InvalidValueException();
readIter!V(&iter).assertEqual(v1);
readIter!short(&iter).assertEqual(v2.get!short);
} }

View file

@ -48,15 +48,20 @@ class TypeMismatchException : Exception {
) pure nothrow @safe { ) pure nothrow @safe {
_expectedType = expectedType; _expectedType = expectedType;
_actualType = actualType; _actualType = actualType;
if (expectedType == 'v') {
super("The type of value at the current position in the message is incompatible to the target variant type."
~ " Type code of the value: " ~ cast(char) actualType);
} else {
super("The type of value at the current position in the message does not match the type of value to be read." super("The type of value at the current position in the message does not match the type of value to be read."
~ " Expected: " ~ cast(char) expectedType ~ ", Got: " ~ cast(char) actualType); ~ " Expected: " ~ cast(char) expectedType ~ ", Got: " ~ cast(char) actualType);
} }
}
int expectedType() @property pure const nothrow @nogc { int expectedType() @property pure const nothrow @safe @nogc {
return _expectedType; return _expectedType;
} }
int actualType() @property pure const nothrow @nogc { int actualType() @property pure const nothrow @safe @nogc {
return _actualType; return _actualType;
} }

View file

@ -4,6 +4,7 @@ import ddbus.thin;
import std.typecons; import std.typecons;
import std.range; import std.range;
import std.traits; import std.traits;
import std.variant : VariantN;
struct DictionaryEntry(K, V) { struct DictionaryEntry(K, V) {
K key; K key;
@ -62,6 +63,9 @@ template canDBus(T) {
enum canDBus = true; enum canDBus = true;
} else static if(isVariant!T) { } else static if(isVariant!T) {
enum canDBus = canDBus!(VariantType!T); enum canDBus = canDBus!(VariantType!T);
} else static if(isInstanceOf!(VariantN, T)) {
// Phobos-style variants are supported if limited to DBus compatible types.
enum canDBus = (T.AllowedTypes.length > 0) && allCanDBus!(T.AllowedTypes);
} else static if(isTuple!T) { } else static if(isTuple!T) {
enum canDBus = allCanDBus!(T.Types); enum canDBus = allCanDBus!(T.Types);
} else static if(isInputRange!T) { } else static if(isInputRange!T) {
@ -112,7 +116,7 @@ string typeSig(T)() if(canDBus!T) {
return "s"; return "s";
} else static if(is(T == ObjectPath)) { } else static if(is(T == ObjectPath)) {
return "o"; return "o";
} else static if(isVariant!T) { } else static if(isVariant!T || isInstanceOf!(VariantN, T)) {
return "v"; return "v";
} else static if(is(T B == enum)) { } else static if(is(T B == enum)) {
return typeSig!B; return typeSig!B;
@ -212,6 +216,9 @@ unittest {
typeSig!(DictionaryEntry!(string, int)[]).assertEqual("a{si}"); typeSig!(DictionaryEntry!(string, int)[]).assertEqual("a{si}");
// multiple arguments // multiple arguments
typeSigAll!(int,bool).assertEqual("ib"); typeSigAll!(int,bool).assertEqual("ib");
// Phobos-style variants
canDBus!(std.variant.Variant).assertFalse();
typeSig!(std.variant.Algebraic!(int, double, string)).assertEqual("v");
// type codes // type codes
typeCode!int().assertEqual(cast(int)('i')); typeCode!int().assertEqual(cast(int)('i'));
typeCode!bool().assertEqual(cast(int)('b')); typeCode!bool().assertEqual(cast(int)('b'));