Merge pull request #10 from WebFreak001/any-types
Support for dynamic variants (fix #9)
This commit is contained in:
commit
1e6d34d607
|
@ -67,8 +67,9 @@ import ddbus;
|
||||||
MessageRouter router = new MessageRouter();
|
MessageRouter router = new MessageRouter();
|
||||||
// create a pattern to register a handler at a path, interface and method
|
// create a pattern to register a handler at a path, interface and method
|
||||||
MessagePattern patt = MessagePattern("/root","ca.thume.test","test");
|
MessagePattern patt = MessagePattern("/root","ca.thume.test","test");
|
||||||
router.setHandler!(int,int)(patt,(int par) {
|
router.setHandler!(int,int,Variant!DBusAny)(patt,(int par, Variant!DBusAny anyArgument) {
|
||||||
writeln("Called with ", par);
|
// anyArgument can contain any type now, it must be specified as argument using Variant!DBusAny.
|
||||||
|
writeln("Called with ", par, ", ", anyArgument);
|
||||||
return par;
|
return par;
|
||||||
});
|
});
|
||||||
// handle a signal
|
// handle a signal
|
||||||
|
|
|
@ -2,6 +2,7 @@ module ddbus.conv;
|
||||||
|
|
||||||
import ddbus.c_lib;
|
import ddbus.c_lib;
|
||||||
import ddbus.util;
|
import ddbus.util;
|
||||||
|
import ddbus.thin;
|
||||||
import std.string;
|
import std.string;
|
||||||
import std.typecons;
|
import std.typecons;
|
||||||
import std.range;
|
import std.range;
|
||||||
|
@ -31,6 +32,52 @@ void buildIter(TS...)(DBusMessageIter *iter, TS args) if(allCanDBus!TS) {
|
||||||
dbus_message_iter_close_container(iter, &sub);
|
dbus_message_iter_close_container(iter, &sub);
|
||||||
} else static if(isAssociativeArray!T) {
|
} else static if(isAssociativeArray!T) {
|
||||||
buildIter(iter, arg.byDictionaryEntries);
|
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') {
|
||||||
|
buildIter(sub, val.str);
|
||||||
|
} else if(val.type == 'b') {
|
||||||
|
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') {
|
||||||
|
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) {
|
} else static if(isVariant!T) {
|
||||||
DBusMessageIter sub;
|
DBusMessageIter sub;
|
||||||
const(char)* subSig = typeSig!(VariantType!T).toStringz();
|
const(char)* subSig = typeSig!(VariantType!T).toStringz();
|
||||||
|
@ -51,11 +98,17 @@ void buildIter(TS...)(DBusMessageIter *iter, TS args) if(allCanDBus!TS) {
|
||||||
|
|
||||||
T readIter(T)(DBusMessageIter *iter) if (canDBus!T) {
|
T readIter(T)(DBusMessageIter *iter) if (canDBus!T) {
|
||||||
T ret;
|
T ret;
|
||||||
static if(!isVariant!T) {
|
static if(!isVariant!T || is(T == Variant!DBusAny)) {
|
||||||
if(dbus_message_iter_get_arg_type(iter) == 'v') {
|
if(dbus_message_iter_get_arg_type(iter) == 'v') {
|
||||||
DBusMessageIter sub;
|
DBusMessageIter sub;
|
||||||
dbus_message_iter_recurse(iter, &sub);
|
dbus_message_iter_recurse(iter, &sub);
|
||||||
|
static if(is(T == Variant!DBusAny)) {
|
||||||
|
ret = variant(readIter!DBusAny(&sub));
|
||||||
|
} else {
|
||||||
ret = readIter!T(&sub);
|
ret = readIter!T(&sub);
|
||||||
|
static if(is(T == DBusAny))
|
||||||
|
ret.explicitVariant = true;
|
||||||
|
}
|
||||||
dbus_message_iter_next(iter);
|
dbus_message_iter_next(iter);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -64,7 +117,7 @@ T readIter(T)(DBusMessageIter *iter) if (canDBus!T) {
|
||||||
assert(dbus_message_iter_get_arg_type(iter) == 'r');
|
assert(dbus_message_iter_get_arg_type(iter) == 'r');
|
||||||
} else static if(is(T == DictionaryEntry!(K1, V1), K1, V1)) {
|
} else static if(is(T == DictionaryEntry!(K1, V1), K1, V1)) {
|
||||||
assert(dbus_message_iter_get_arg_type(iter) == 'e');
|
assert(dbus_message_iter_get_arg_type(iter) == 'e');
|
||||||
} else {
|
} else static if(!is(T == DBusAny) && !is(T == Variant!DBusAny)) {
|
||||||
assert(dbus_message_iter_get_arg_type(iter) == typeCode!T());
|
assert(dbus_message_iter_get_arg_type(iter) == typeCode!T());
|
||||||
}
|
}
|
||||||
static if(is(T==string)) {
|
static if(is(T==string)) {
|
||||||
|
@ -102,6 +155,40 @@ T readIter(T)(DBusMessageIter *iter) if (canDBus!T) {
|
||||||
auto entry = readIter!(DictionaryEntry!(KeyType!T, ValueType!T))(&sub);
|
auto entry = readIter!(DictionaryEntry!(KeyType!T, ValueType!T))(&sub);
|
||||||
ret[entry.key] = entry.value;
|
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') {
|
||||||
|
ret.str = readIter!string(iter);
|
||||||
|
} else if(ret.type == 'b') {
|
||||||
|
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') {
|
||||||
|
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) {
|
} else static if(basicDBus!T) {
|
||||||
dbus_message_iter_get_basic(iter, &ret);
|
dbus_message_iter_get_basic(iter, &ret);
|
||||||
}
|
}
|
||||||
|
@ -123,9 +210,22 @@ unittest {
|
||||||
bool[] emptyB;
|
bool[] emptyB;
|
||||||
string[string] map;
|
string[string] map;
|
||||||
map["hello"] = "world";
|
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));
|
||||||
|
Variant!DBusAny complexVar = variant(DBusAny([
|
||||||
|
"hello world": variant(DBusAny(1337)),
|
||||||
|
"array value": variant(DBusAny([42, 64])),
|
||||||
|
"tuple value": variant(tupleMember)
|
||||||
|
]));
|
||||||
|
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.build(args.expand);
|
||||||
msg.signature().assertEqual("ibsvai(diaasabv)a{ss}");
|
msg.signature().assertEqual("ibsvai(diaasabv)a{ss}tv");
|
||||||
msg.readTuple!(typeof(args))().assertEqual(args);
|
msg.readTuple!(typeof(args))().assertEqual(args);
|
||||||
DBusMessageIter iter;
|
DBusMessageIter iter;
|
||||||
dbus_message_iter_init(msg.msg, &iter);
|
dbus_message_iter_init(msg.msg, &iter);
|
||||||
|
@ -136,4 +236,6 @@ unittest {
|
||||||
readIter!(int[])(&iter).assertEqual([6,5]);
|
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!(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!(string[string])(&iter).assertEqual(["hello": "world"]);
|
||||||
|
readIter!DBusAny(&iter).assertEqual(anyVar);
|
||||||
|
readIter!(Variant!DBusAny)(&iter).assertEqual(complexVar);
|
||||||
}
|
}
|
||||||
|
|
|
@ -202,17 +202,24 @@ unittest{
|
||||||
router.setHandler!(void,int,string)(patt,(int p, string p2) {});
|
router.setHandler!(void,int,string)(patt,(int p, string p2) {});
|
||||||
patt = MessagePattern("/root/wat","ca.thume.tester","lolwut");
|
patt = MessagePattern("/root/wat","ca.thume.tester","lolwut");
|
||||||
router.setHandler!(int,int)(patt,(int p) {return 6;});
|
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");
|
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");
|
patt = MessagePattern("/troll","ca.thume.tester","wow");
|
||||||
router.setHandler!(void)(patt,{return;});
|
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
|
// TODO: these tests rely on nondeterministic hash map ordering
|
||||||
static string introspectResult = `<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
static string introspectResult = `<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
||||||
<node name="/root"><interface name="ca.thume.test"><method name="test"><arg type="i" direction="in"/><arg type="i" direction="out"/></method></interface><interface name="ca.thume.tester"><method name="lolwut"><arg type="i" direction="in"/><arg type="s" direction="in"/></method></interface><node name="foo"/><node name="wat"/></node>`;
|
<node name="/root"><interface name="ca.thume.test"><method name="test"><arg type="i" direction="in"/><arg type="i" direction="out"/></method></interface><interface name="ca.thume.tester"><method name="lolwut"><arg type="i" direction="in"/><arg type="s" direction="in"/></method></interface><node name="bar"/><node name="foo"/><node name="wat"/></node>`;
|
||||||
router.introspectXML("/root").assertEqual(introspectResult);
|
router.introspectXML("/root").assertEqual(introspectResult);
|
||||||
static string introspectResult2 = `<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
static string introspectResult2 = `<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
||||||
<node name="/root/foo"><interface name="ca.thume.tester"><method name="lolwut"><arg type="i" direction="in"/><arg type="s" direction="out"/><arg type="s" direction="out"/><arg type="i" direction="out"/></method></interface></node>`;
|
<node name="/root/foo"><interface name="ca.thume.tester"><method name="lolwut"><arg type="i" direction="in"/><arg type="v" direction="in"/><arg type="s" direction="out"/><arg type="s" direction="out"/><arg type="i" direction="out"/></method></interface></node>`;
|
||||||
router.introspectXML("/root/foo").assertEqual(introspectResult2);
|
router.introspectXML("/root/foo").assertEqual(introspectResult2);
|
||||||
router.introspectXML("/").assertEndsWith(`<node name="/"><node name="root"/><node name="troll"/></node>`);
|
router.introspectXML("/").assertEndsWith(`<node name="/"><node name="root"/><node name="troll"/></node>`);
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,7 +74,7 @@ void registerMethods(T : Object)(MessageRouter router, string path, string iface
|
||||||
unittest {
|
unittest {
|
||||||
import dunit.toolkit;
|
import dunit.toolkit;
|
||||||
class Tester {
|
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() {}
|
void wat() {}
|
||||||
@SignalMethod
|
@SignalMethod
|
||||||
void signalRecv() {}
|
void signalRecv() {}
|
||||||
|
@ -91,6 +91,6 @@ unittest {
|
||||||
patt.signal = false;
|
patt.signal = false;
|
||||||
router.callTable.assertHasKey(patt);
|
router.callTable.assertHasKey(patt);
|
||||||
auto res = router.callTable[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"]);
|
res.retSig.assertEqual(["i"]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,10 @@ import ddbus.util;
|
||||||
import std.string;
|
import std.string;
|
||||||
import std.typecons;
|
import std.typecons;
|
||||||
import std.exception;
|
import std.exception;
|
||||||
|
import std.traits;
|
||||||
|
import std.conv;
|
||||||
|
import std.range;
|
||||||
|
import std.algorithm;
|
||||||
|
|
||||||
class DBusException : Exception {
|
class DBusException : Exception {
|
||||||
this(DBusError *err) {
|
this(DBusError *err) {
|
||||||
|
@ -26,7 +30,340 @@ T wrapErrors(T)(T delegate(DBusError *err) del) {
|
||||||
return ret;
|
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';
|
||||||
|
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)
|
||||||
|
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, 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(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;
|
||||||
|
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(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;
|
||||||
|
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 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) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
struct Variant(T) {
|
||||||
|
///
|
||||||
T data;
|
T data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,7 +52,7 @@ template basicDBus(T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
template canDBus(T) {
|
template canDBus(T) {
|
||||||
static if(basicDBus!T) {
|
static if(basicDBus!T || is(T == DBusAny)) {
|
||||||
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);
|
||||||
|
@ -101,6 +101,8 @@ string typeSig(T)() if(canDBus!T) {
|
||||||
return "s";
|
return "s";
|
||||||
} else static if(isVariant!T) {
|
} else static if(isVariant!T) {
|
||||||
return "v";
|
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) {
|
} else static if(isTuple!T) {
|
||||||
string sig = "(";
|
string sig = "(";
|
||||||
foreach(i, S; T.Types) {
|
foreach(i, S; T.Types) {
|
||||||
|
|
Loading…
Reference in a new issue