diff --git a/source/ddbus/conv.d b/source/ddbus/conv.d new file mode 100644 index 0000000..72cd5be --- /dev/null +++ b/source/ddbus/conv.d @@ -0,0 +1,92 @@ +module ddbus.conv; + +import ddbus.c_lib; +import ddbus.util; +import std.string; +import std.typecons; +import std.range; + +void buildIter(TS...)(DBusMessageIter *iter, TS args) if(allCanDBus!TS) { + foreach(index, arg; args) { + alias TS[index] T; + static if(is(T == string)) { + immutable(char)* cStr = arg.toStringz(); + dbus_message_iter_append_basic(iter,typeCode!T,&cStr); + } else static if(is(T==bool)) { + dbus_bool_t longerBool = arg; // dbus bools are ints + dbus_message_iter_append_basic(iter,typeCode!T,&longerBool); + } else static if(isTuple!T) { + DBusMessageIter sub; + dbus_message_iter_open_container(iter, 'r', null, &sub); + buildIter(&sub, arg.expand); + dbus_message_iter_close_container(iter, &sub); + } else static if(isInputRange!T) { + DBusMessageIter sub; + const(char)* subSig = (typeSig!(ElementType!T)()).toStringz(); + dbus_message_iter_open_container(iter, 'a', subSig, &sub); + foreach(x; arg) { + buildIter(&sub, x); + } + dbus_message_iter_close_container(iter, &sub); + } else static if(basicDBus!T) { + dbus_message_iter_append_basic(iter,typeCode!T,&arg); + } + } +} + +T readIter(T)(DBusMessageIter *iter) if (canDBus!T) { + T ret; + static if(isTuple!T) { + assert(dbus_message_iter_get_arg_type(iter) == 'r'); + } else { + assert(dbus_message_iter_get_arg_type(iter) == typeCode!T()); + } + static if(is(T==string)) { + const(char)* cStr; + dbus_message_iter_get_basic(iter, &cStr); + ret = cStr.fromStringz().idup; // copy string + } else static if(is(T==bool)) { + dbus_bool_t longerBool; + dbus_message_iter_get_basic(iter, &longerBool); + ret = cast(bool)longerBool; + } else static if(isTuple!T) { + DBusMessageIter sub; + dbus_message_iter_recurse(iter, &sub); + readIterTuple!T(&sub, ret); + } else static if(is(T t : U[], U)) { + assert(dbus_message_iter_get_element_type(iter) == typeCode!U); + DBusMessageIter sub; + dbus_message_iter_recurse(iter, &sub); + while(dbus_message_iter_get_arg_type(&sub) != 0) { + ret ~= readIter!U(&sub); + } + } else static if(basicDBus!T) { + dbus_message_iter_get_basic(iter, &ret); + } + dbus_message_iter_next(iter); + return ret; +} + +void readIterTuple(Tup)(DBusMessageIter *iter, ref Tup tuple) if(isTuple!Tup && allCanDBus!(Tup.Types)) { + foreach(index, T; Tup.Types) { + tuple[index] = readIter!T(iter); + } +} + +unittest { + import dunit.toolkit; + import ddbus.thin; + Message msg = new Message("org.example.wow","/wut","org.test.iface","meth"); + bool[] emptyB; + auto args = tuple(5,true,"wow",[6,5],tuple(6.2,4,[["lol"]],emptyB)); + msg.build(args.expand); + msg.signature().assertEqual("ibsai(diaasab)"); + msg.readTuple!(typeof(args))().assertEqual(args); + DBusMessageIter iter; + dbus_message_iter_init(msg.msg, &iter); + readIter!int(&iter).assertEqual(5); + readIter!bool(&iter).assertEqual(true); + readIter!string(&iter).assertEqual("wow"); + readIter!(int[])(&iter).assertEqual([6,5]); + readIter!(Tuple!(double,int,string[][],bool[]))(&iter).assertEqual(tuple(6.2,4,[["lol"]],emptyB)); +} diff --git a/source/ddbus/thin.d b/source/ddbus/thin.d index 2e56e68..7c7d1ec 100644 --- a/source/ddbus/thin.d +++ b/source/ddbus/thin.d @@ -1,9 +1,12 @@ module ddbus.thin; import ddbus.c_lib; +import ddbus.conv; +import ddbus.util; import std.string; +import std.typecons; -struct Message { +class Message { this(string dest, string path, string iface, string method) { msg = dbus_message_new_method_call(dest.toStringz(), path.toStringz(), iface.toStringz(), method.toStringz()); } @@ -12,5 +15,44 @@ struct Message { dbus_message_unref(msg); } + void build(TS...)(TS args) if(allCanDBus!TS) { + DBusMessageIter iter; + dbus_message_iter_init_append(msg, &iter); + buildIter(&iter, args); + } + + T read(T)() if(canDBus!T) { + DBusMessageIter iter; + dbus_message_iter_init(msg, &iter); + return readIter!T(&iter); + } + + Tup readTuple(Tup)() if(isTuple!Tup && allCanDBus!(Tup.Types)) { + DBusMessageIter iter; + dbus_message_iter_init(msg, &iter); + Tup ret; + readIterTuple(&iter, ret); + return ret; + } + + const(char)[] signature() { + const(char)* cSig = dbus_message_get_signature(msg); + return fromStringz(cSig); + } + DBusMessage *msg; } + +// class MessageBuilder { +// DBusMessageIter iter; +// // kept around for GC reasons, iterators need parent message. +// Message parent; +// this(Message msg) { +// parent = msg; +// dbus_message_iter_init(parent.msg, &iter); +// } + +// void doBasic(int type, void *v) { +// dbus_message_iter_append_basic(&iter, type, v); +// } +// } diff --git a/source/ddbus/util.d b/source/ddbus/util.d index 29ccb12..04560ef 100644 --- a/source/ddbus/util.d +++ b/source/ddbus/util.d @@ -12,12 +12,19 @@ template allCanDBus(TS...) { } } -template canDBus(T) { +template basicDBus(T) { static if(is(T == byte) || is(T == short) || is (T == ushort) || is (T == int) || is (T == uint) || is (T == long) || is (T == ulong) || is (T == double) || is (T == string) || is(T == bool)) { + enum basicDBus = true; + } else { + enum basicDBus = false; + } +} + +template canDBus(T) { + static if(basicDBus!T) { enum canDBus = true; - } else static if(isTuple!T) { enum canDBus = allCanDBus!(T.Types); } else static if(isInputRange!T) { @@ -77,6 +84,11 @@ string typeSigAll(TS...)() if(allCanDBus!TS) { return sig; } +int typeCode(T)() if(canDBus!T) { + string sig = typeSig!T(); + return sig[0]; +} + unittest { import dunit.toolkit; // basics @@ -91,6 +103,9 @@ unittest { typeSig!(Tuple!(byte)[][]).assertEqual("aa(y)"); // multiple arguments typeSigAll!(int,bool).assertEqual("ib"); + // type codes + typeCode!int().assertEqual(cast(int)('i')); + typeCode!bool().assertEqual(cast(int)('b')); // ctfe-capable static string sig = typeSig!ulong(); sig.assertEqual("t");