Improved code formatting (#33)

- Used dfmt (otbs style)
- Added some empty lines and braces on all block statements
- Add .editorconfig

Closes #29
This commit is contained in:
Harry Vennik 2017-12-18 19:19:27 +01:00 committed by thaven
parent c42eb823a2
commit 137b3c36b7
10 changed files with 723 additions and 542 deletions

8
.editorconfig Normal file
View file

@ -0,0 +1,8 @@
[**.d]
indent_style = space
indent_size = 2
dfmt_brace_style = otbs
dfmt_space_after_keywords = true
dfmt_space_after_cast = true
dfmt_template_constraint_style = always_newline_indent

View file

@ -10,10 +10,16 @@ import std.typecons : BitFlags, Flag;
Default is to include public fields only Default is to include public fields only
+/ +/
enum MarshalingFlag : ubyte { enum MarshalingFlag : ubyte {
includePrivateFields = 1 << 0, /// Automatically include private fields /++
manualOnly = 1 << 7 /// Only include fields with explicit Automatically include private fields
/// `@Yes.DBusMarshal`. This overrides any +/
/// `include` flags. includePrivateFields = 1 << 0,
/++
Only include fields with explicit `@Yes.DBusMarshal`. This overrides any
`include` flags.
+/
manualOnly = 1 << 7
} }
/++ /++
@ -31,35 +37,38 @@ package(ddbus) template isAllowedField(alias field) {
static if (UDAs.length != 0) { static if (UDAs.length != 0) {
static assert(UDAs.length == 1, static assert(UDAs.length == 1,
"Only one UDA of type Flag!\"DBusMarshal\" allowed on struct field."); "Only one UDA of type Flag!\"DBusMarshal\" allowed on struct field.");
static assert(is(typeof(UDAs[0]) == Flag!"DBusMarshal"), static assert(is(typeof(UDAs[0]) == Flag!"DBusMarshal"),
"Did you intend to add UDA Yes.DBusMarshal or No.DBusMarshal?"); "Did you intend to add UDA Yes.DBusMarshal or No.DBusMarshal?");
enum isAllowedField = cast(bool) UDAs[0]; enum isAllowedField = cast(bool) UDAs[0];
} else static if (!(flags & MarshalingFlag.manualOnly)) { } else static if (!(flags & MarshalingFlag.manualOnly)) {
static if (__traits(getProtection, field) == "public") static if (__traits(getProtection, field) == "public") {
enum isAllowedField = true; enum isAllowedField = true;
else static if (cast(bool) (flags & MarshalingFlag.includePrivateFields)) } else static if (cast(bool)(flags & MarshalingFlag.includePrivateFields)) {
enum isAllowedField = true; enum isAllowedField = true;
else } else {
enum isAllowedField = false; enum isAllowedField = false;
} else }
} else {
enum isAllowedField = false; enum isAllowedField = false;
} }
}
private template isMarshalingFlag(T) { private template isMarshalingFlag(T) {
enum isMarshalingFlag = is(T == MarshalingFlag); enum isMarshalingFlag = is(T == MarshalingFlag);
} }
private template marshalingFlags(S) if (is(S == struct)) { private template marshalingFlags(S)
if (is(S == struct)) {
private alias getUDAs!(S, BitFlags!MarshalingFlag) UDAs; private alias getUDAs!(S, BitFlags!MarshalingFlag) UDAs;
static if (UDAs.length == 0) static if (UDAs.length == 0) {
enum marshalingFlags = BitFlags!MarshalingFlag.init; enum marshalingFlags = BitFlags!MarshalingFlag.init;
else { } else {
static assert (UDAs.length == 1, static assert(UDAs.length == 1, "Only one @dbusMarshaling UDA allowed on type.");
"Only one @dbusMarshaling UDA allowed on type.");
static assert(is(typeof(UDAs[0]) == BitFlags!MarshalingFlag), static assert(is(typeof(UDAs[0]) == BitFlags!MarshalingFlag),
"Huh? Did you intend to use @dbusMarshaling UDA?"); "Huh? Did you intend to use @dbusMarshaling UDA?");
enum marshalingFlags = UDAs[0]; enum marshalingFlags = UDAs[0];
} }
} }

View file

@ -10,7 +10,9 @@ enum BusPath = "/org/freedesktop/DBus";
enum BusInterface = "org.freedesktop.DBus"; enum BusInterface = "org.freedesktop.DBus";
enum NameFlags { enum NameFlags {
AllowReplace = 1, ReplaceExisting = 2, NoQueue = 4 AllowReplace = 1,
ReplaceExisting = 2,
NoQueue = 4
} }
/// Requests a DBus well-known name. /// Requests a DBus well-known name.
@ -28,7 +30,8 @@ bool requestName(Connection conn, string name,
/// and isn't guaranteed to work with other tasks and threads. /// and isn't guaranteed to work with other tasks and threads.
/// Use only for apps that only do DBus triggered things. /// Use only for apps that only do DBus triggered things.
void simpleMainLoop(Connection conn) { void simpleMainLoop(Connection conn) {
while(dbus_connection_read_write_dispatch(conn.conn, -1)) {} // empty loop body while (dbus_connection_read_write_dispatch(conn.conn, -1)) {
} // empty loop body
} }
/// Single tick in the DBus connection which can be used for /// Single tick in the DBus connection which can be used for
@ -39,7 +42,7 @@ bool tick(Connection conn) {
unittest { unittest {
import dunit.toolkit; import dunit.toolkit;
Connection conn = connectToBus(); Connection conn = connectToBus();
conn.requestName("ca.thume.ddbus.testing").assertTrue(); conn.requestName("ca.thume.ddbus.testing").assertTrue();
} }

View file

@ -1,6 +1,10 @@
module ddbus.c_lib; module ddbus.c_lib;
import core.stdc.config; import core.stdc.config;
import core.stdc.stdarg; import core.stdc.stdarg;
// dfmt off
extern (C): extern (C):
// START dbus/dbus-arch-deps.d // START dbus/dbus-arch-deps.d
alias c_long dbus_int64_t; alias c_long dbus_int64_t;

View file

@ -14,7 +14,8 @@ import std.range;
import std.traits; import std.traits;
import std.variant : VariantN; 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) {
alias TS[index] T; alias TS[index] T;
static if (is(T == string)) { static if (is(T == string)) {
@ -60,8 +61,7 @@ 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 (isInstanceOf!(VariantN, T)) { } else static if (isInstanceOf!(VariantN, T)) {
enforce(arg.hasValue, enforce(arg.hasValue, new InvalidValueException(arg, "dbus:" ~ cast(char) typeCode!T));
new InvalidValueException(arg, "dbus:" ~ cast(char) typeCode!T));
DBusMessageIter sub; DBusMessageIter sub;
foreach (AT; T.AllowedTypes) { foreach (AT; T.AllowedTypes) {
@ -82,15 +82,20 @@ void buildIter(TS...)(DBusMessageIter *iter, TS args) if(allCanDBus!TS) {
DBusMessageIter subStore; DBusMessageIter subStore;
DBusMessageIter* sub = &subStore; DBusMessageIter* sub = &subStore;
const(char)[] sig = [cast(char) val.type]; const(char)[] sig = [cast(char) val.type];
if(val.type == 'a') if (val.type == 'a') {
sig ~= val.signature; sig ~= val.signature;
else if(val.type == 'r') } else if (val.type == 'r') {
sig = val.signature; sig = val.signature;
}
sig ~= '\0'; sig ~= '\0';
if (!val.explicitVariant)
if (!val.explicitVariant) {
sub = iter; sub = iter;
else } else {
dbus_message_iter_open_container(iter, 'v', sig.ptr, sub); dbus_message_iter_open_container(iter, 'v', sig.ptr, sub);
}
if (val.type == 's') { if (val.type == 's') {
buildIter(sub, val.str); buildIter(sub, val.str);
} else if (val.type == 'o') { } else if (val.type == 'o') {
@ -102,18 +107,26 @@ void buildIter(TS...)(DBusMessageIter *iter, TS args) if(allCanDBus!TS) {
} else if (val.type == 'a') { } else if (val.type == 'a') {
DBusMessageIter arr; DBusMessageIter arr;
dbus_message_iter_open_container(sub, 'a', sig[1 .. $].ptr, &arr); dbus_message_iter_open_container(sub, 'a', sig[1 .. $].ptr, &arr);
if (val.signature == ['y'])
foreach (item; val.binaryData) if (val.signature == ['y']) {
foreach (item; val.binaryData) {
dbus_message_iter_append_basic(&arr, 'y', &item); dbus_message_iter_append_basic(&arr, 'y', &item);
else }
foreach(item; val.array) } else {
foreach (item; val.array) {
buildIter(&arr, item); buildIter(&arr, item);
}
}
dbus_message_iter_close_container(sub, &arr); dbus_message_iter_close_container(sub, &arr);
} else if (val.type == 'r') { } else if (val.type == 'r') {
DBusMessageIter arr; DBusMessageIter arr;
dbus_message_iter_open_container(sub, 'r', null, &arr); dbus_message_iter_open_container(sub, 'r', null, &arr);
foreach(item; val.tuple)
foreach (item; val.tuple) {
buildIter(&arr, item); buildIter(&arr, item);
}
dbus_message_iter_close_container(sub, &arr); dbus_message_iter_close_container(sub, &arr);
} else if (val.type == 'e') { } else if (val.type == 'e') {
DBusMessageIter entry; DBusMessageIter entry;
@ -122,8 +135,10 @@ void buildIter(TS...)(DBusMessageIter *iter, TS args) if(allCanDBus!TS) {
buildIter(&entry, val.entry.value); buildIter(&entry, val.entry.value);
dbus_message_iter_close_container(sub, &entry); dbus_message_iter_close_container(sub, &entry);
} }
if(val.explicitVariant)
if (val.explicitVariant) {
dbus_message_iter_close_container(iter, sub); dbus_message_iter_close_container(iter, sub);
}
} else static if (isInstanceOf!(Variant, T)) { } else static if (isInstanceOf!(Variant, T)) {
DBusMessageIter sub; DBusMessageIter sub;
const(char)* subSig = typeSig!(VariantType!T).toStringz(); const(char)* subSig = typeSig!(VariantType!T).toStringz();
@ -156,20 +171,19 @@ void buildIter(TS...)(DBusMessageIter *iter, TS args) if(allCanDBus!TS) {
} }
} }
T readIter(T)(DBusMessageIter *iter) if (is(T == enum)) { T readIter(T)(DBusMessageIter* iter)
if (is(T == enum)) {
import std.algorithm.searching : canFind; import std.algorithm.searching : canFind;
alias OriginalType!T B; alias OriginalType!T B;
B value = readIter!B(iter); B value = readIter!B(iter);
enforce( enforce(only(EnumMembers!T).canFind(value), new InvalidValueException(value, T.stringof));
only(EnumMembers!T).canFind(value),
new InvalidValueException(value, T.stringof)
);
return cast(T) value; return cast(T) value;
} }
T readIter(T)(DBusMessageIter *iter) if (isInstanceOf!(BitFlags, T)) { T readIter(T)(DBusMessageIter* iter)
if (isInstanceOf!(BitFlags, T)) {
import std.algorithm.iteration : fold; import std.algorithm.iteration : fold;
alias TemplateArgsOf!T[0] E; alias TemplateArgsOf!T[0] E;
@ -178,15 +192,13 @@ T readIter(T)(DBusMessageIter *iter) if (isInstanceOf!(BitFlags, T)) {
B mask = only(EnumMembers!E).fold!((a, b) => cast(B)(a | b)); B mask = only(EnumMembers!E).fold!((a, b) => cast(B)(a | b));
B value = readIter!B(iter); B value = readIter!B(iter);
enforce( enforce(!(value & ~mask), new InvalidValueException(value, T.stringof));
!(value & ~mask),
new InvalidValueException(value, T.stringof)
);
return T(cast(E) value); return T(cast(E) value);
} }
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); auto argType = dbus_message_iter_get_arg_type(iter);
T ret; T ret;
@ -206,22 +218,19 @@ T readIter(T)(DBusMessageIter *iter) if (!is(T == enum) && !isInstanceOf!(BitFla
} }
} }
static if( static if (!is(T == DBusAny) && !is(T == Variant!DBusAny) && !isInstanceOf!(VariantN, T)) {
!is(T == DBusAny) enforce(argType == typeCode!T(), new TypeMismatchException(typeCode!T(), argType));
&& !is(T == Variant!DBusAny)
&& !isInstanceOf!(VariantN, T)
) {
enforce(argType == typeCode!T(),
new TypeMismatchException(typeCode!T(), argType));
} }
static if (is(T == string) || is(T == ObjectPath)) { static if (is(T == string) || is(T == ObjectPath)) {
const(char)* cStr; const(char)* cStr;
dbus_message_iter_get_basic(iter, &cStr); dbus_message_iter_get_basic(iter, &cStr);
string str = cStr.fromStringz().idup; // copy string string str = cStr.fromStringz().idup; // copy string
static if(is(T==string)) static if (is(T == string)) {
ret = str; ret = str;
else } else {
ret = ObjectPath(str); ret = ObjectPath(str);
}
} else static if (is(T == bool)) { } else static if (is(T == bool)) {
dbus_bool_t longerBool; dbus_bool_t longerBool;
dbus_message_iter_get_basic(iter, &longerBool); dbus_message_iter_get_basic(iter, &longerBool);
@ -232,8 +241,10 @@ T readIter(T)(DBusMessageIter *iter) if (!is(T == enum) && !isInstanceOf!(BitFla
readIterTuple!T(&sub, ret); readIterTuple!T(&sub, ret);
} else static if (is(T t : U[], U)) { } else static if (is(T t : U[], U)) {
assert(dbus_message_iter_get_element_type(iter) == typeCode!U); assert(dbus_message_iter_get_element_type(iter) == typeCode!U);
DBusMessageIter sub; DBusMessageIter sub;
dbus_message_iter_recurse(iter, &sub); dbus_message_iter_recurse(iter, &sub);
while (dbus_message_iter_get_arg_type(&sub) != 0) { while (dbus_message_iter_get_arg_type(&sub) != 0) {
static if (is(U == DictionaryEntry!(K, V), K, V)) { static if (is(U == DictionaryEntry!(K, V), K, V)) {
DBusMessageIter entry; DBusMessageIter entry;
@ -249,8 +260,7 @@ T readIter(T)(DBusMessageIter *iter) if (!is(T == enum) && !isInstanceOf!(BitFla
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)) { } else static if (isInstanceOf!(VariantN, T)) {
scope const(char)[] argSig = scope const(char)[] argSig = dbus_message_iter_get_signature(iter).fromStringz();
dbus_message_iter_get_signature(iter).fromStringz();
scope (exit) scope (exit)
dbus_free(cast(void*) argSig.ptr); dbus_free(cast(void*) argSig.ptr);
@ -270,6 +280,7 @@ T readIter(T)(DBusMessageIter *iter) if (!is(T == enum) && !isInstanceOf!(BitFla
} 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);
while (dbus_message_iter_get_arg_type(&sub) != 0) { while (dbus_message_iter_get_arg_type(&sub) != 0) {
DBusMessageIter entry; DBusMessageIter entry;
dbus_message_iter_recurse(&sub, &entry); dbus_message_iter_recurse(&sub, &entry);
@ -281,6 +292,7 @@ T readIter(T)(DBusMessageIter *iter) if (!is(T == enum) && !isInstanceOf!(BitFla
} else static if (is(T == DBusAny)) { } else static if (is(T == DBusAny)) {
ret.type = argType; 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);
return ret; return ret;
@ -298,7 +310,7 @@ T readIter(T)(DBusMessageIter *iter) if (!is(T == enum) && !isInstanceOf!(BitFla
auto sig = dbus_message_iter_get_signature(&sub); auto sig = dbus_message_iter_get_signature(&sub);
ret.signature = sig.fromStringz.dup; ret.signature = sig.fromStringz.dup;
dbus_free(sig); dbus_free(sig);
if (ret.signature == ['y']) if (ret.signature == ['y']) {
while (dbus_message_iter_get_arg_type(&sub) != 0) { while (dbus_message_iter_get_arg_type(&sub) != 0) {
ubyte b; ubyte b;
assert(dbus_message_iter_get_arg_type(&sub) == 'y'); assert(dbus_message_iter_get_arg_type(&sub) == 'y');
@ -306,22 +318,26 @@ T readIter(T)(DBusMessageIter *iter) if (!is(T == enum) && !isInstanceOf!(BitFla
dbus_message_iter_next(&sub); dbus_message_iter_next(&sub);
ret.binaryData ~= b; ret.binaryData ~= b;
} }
else } else {
while (dbus_message_iter_get_arg_type(&sub) != 0) { while (dbus_message_iter_get_arg_type(&sub) != 0) {
ret.array ~= readIter!DBusAny(&sub); ret.array ~= readIter!DBusAny(&sub);
} }
}
} else if (ret.type == 'r') { } else if (ret.type == 'r') {
auto sig = dbus_message_iter_get_signature(iter); auto sig = dbus_message_iter_get_signature(iter);
ret.signature = sig.fromStringz.dup; ret.signature = sig.fromStringz.dup;
dbus_free(sig); dbus_free(sig);
DBusMessageIter sub; DBusMessageIter sub;
dbus_message_iter_recurse(iter, &sub); dbus_message_iter_recurse(iter, &sub);
while (dbus_message_iter_get_arg_type(&sub) != 0) { while (dbus_message_iter_get_arg_type(&sub) != 0) {
ret.tuple ~= readIter!DBusAny(&sub); ret.tuple ~= readIter!DBusAny(&sub);
} }
} else if (ret.type == 'e') { } else if (ret.type == 'e') {
DBusMessageIter sub; DBusMessageIter sub;
dbus_message_iter_recurse(iter, &sub); dbus_message_iter_recurse(iter, &sub);
ret.entry = new DictionaryEntry!(DBusAny, DBusAny); ret.entry = new DictionaryEntry!(DBusAny, DBusAny);
ret.entry.key = readIter!DBusAny(&sub); ret.entry.key = readIter!DBusAny(&sub);
ret.entry.value = readIter!DBusAny(&sub); ret.entry.value = readIter!DBusAny(&sub);
@ -338,14 +354,15 @@ T readIter(T)(DBusMessageIter *iter) if (!is(T == enum) && !isInstanceOf!(BitFla
return ret; return ret;
} }
void readIterTuple(Tup)(DBusMessageIter *iter, ref Tup tuple) if(isTuple!Tup && allCanDBus!(Tup.Types)) { void readIterTuple(Tup)(DBusMessageIter* iter, ref Tup tuple)
if (isTuple!Tup && allCanDBus!(Tup.Types)) {
foreach (index, T; Tup.Types) { foreach (index, T; Tup.Types) {
tuple[index] = readIter!T(iter); tuple[index] = readIter!T(iter);
} }
} }
void readIterStruct(S)(DBusMessageIter *iter, ref S s) if(is(S == struct) && canDBus!S) void readIterStruct(S)(DBusMessageIter* iter, ref S s)
{ if (is(S == struct) && canDBus!S) {
foreach (index, T; Fields!S) { foreach (index, T; Fields!S) {
static if (isAllowedField!(s.tupleof[index])) { static if (isAllowedField!(s.tupleof[index])) {
s.tupleof[index] = readIter!T(iter); s.tupleof[index] = readIter!T(iter);
@ -356,7 +373,11 @@ void readIterStruct(S)(DBusMessageIter *iter, ref S s) if(is(S == struct) && can
unittest { unittest {
import dunit.toolkit; import dunit.toolkit;
import ddbus.thin; import ddbus.thin;
Variant!T var(T)(T data){ return Variant!T(data); }
Variant!T var(T)(T data) {
return Variant!T(data);
}
Message msg = Message("org.example.wow", "/wut", "org.test.iface", "meth"); Message msg = Message("org.example.wow", "/wut", "org.test.iface", "meth");
bool[] emptyB; bool[] emptyB;
string[string] map; string[string] map;
@ -365,18 +386,18 @@ unittest {
anyVar.type.assertEqual('t'); anyVar.type.assertEqual('t');
anyVar.uint64.assertEqual(1561); anyVar.uint64.assertEqual(1561);
anyVar.explicitVariant.assertEqual(false); anyVar.explicitVariant.assertEqual(false);
auto tupleMember = DBusAny(tuple(Variant!int(45), Variant!ushort(5), 32, [1, 2], tuple(variant(4), 5), map)); auto tupleMember = DBusAny(tuple(Variant!int(45), Variant!ushort(5), 32,
Variant!DBusAny complexVar = variant(DBusAny([ [1, 2], tuple(variant(4), 5), map));
"hello world": variant(DBusAny(1337)), Variant!DBusAny complexVar = variant(DBusAny(["hello world" : variant(DBusAny(1337)),
"array value": variant(DBusAny([42, 64])), "array value" : variant(DBusAny([42, 64])), "tuple value"
"tuple value": variant(tupleMember), : variant(tupleMember), "optimized binary data"
"optimized binary data": variant(DBusAny(cast(ubyte[]) [1, 2, 3, 4, 5, 6])) : variant(DBusAny(cast(ubyte[])[1, 2, 3, 4, 5, 6]))]));
]));
complexVar.data.type.assertEqual('a'); complexVar.data.type.assertEqual('a');
complexVar.data.signature.assertEqual("{sv}".dup); complexVar.data.signature.assertEqual("{sv}".dup);
tupleMember.signature.assertEqual("(vviai(vi)a{ss})"); 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); 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}tv"); msg.signature().assertEqual("ibsvai(diaasabv)a{ss}tv");
@ -392,7 +413,8 @@ unittest {
readIter!string(&iter).assertEqual("wow"); readIter!string(&iter).assertEqual("wow");
readIter!double(&iter).assertEqual(5.9); readIter!double(&iter).assertEqual(5.9);
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])));
// There are two ways to read a dictionary, so duplicate the iterator to test both. // There are two ways to read a dictionary, so duplicate the iterator to test both.
auto iter2 = iter; auto iter2 = iter;
@ -412,8 +434,18 @@ unittest {
import std.variant : Algebraic; import std.variant : Algebraic;
enum E : int { a, b, c } enum E : int {
enum F : uint { x = 1, y = 2, z = 4 } a,
b,
c
}
enum F : uint {
x = 1,
y = 2,
z = 4
}
alias V = Algebraic!(byte, short, int, long, string); 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");
@ -437,4 +469,3 @@ unittest {
readIter!V(&iter).assertEqual(v1); readIter!V(&iter).assertEqual(v1);
readIter!short(&iter).assertEqual(v2.get!short); readIter!short(&iter).assertEqual(v2.get!short);
} }

View file

@ -2,12 +2,8 @@ module ddbus.exception;
import ddbus.c_lib; import ddbus.c_lib;
package T wrapErrors(T)( package T wrapErrors(T)(T delegate(DBusError* err) del, string file = __FILE__,
T delegate(DBusError *err) del, size_t line = __LINE__, Throwable next = null) {
string file = __FILE__,
size_t line = __LINE__,
Throwable next = null
) {
DBusError error; DBusError error;
dbus_error_init(&error); dbus_error_init(&error);
T ret = del(&error); T ret = del(&error);
@ -23,12 +19,8 @@ package T wrapErrors(T)(
Thrown when a DBus error code was returned by libdbus. Thrown when a DBus error code was returned by libdbus.
+/ +/
class DBusException : Exception { class DBusException : Exception {
private this( private this(scope DBusError* err, string file = __FILE__,
scope DBusError *err, size_t line = __LINE__, Throwable next = null) pure nothrow {
string file = __FILE__,
size_t line = __LINE__,
Throwable next = null
) pure nothrow {
import std.string : fromStringz; import std.string : fromStringz;
super(err.message.fromStringz().idup, file, line, next); super(err.message.fromStringz().idup, file, line, next);
@ -41,37 +33,27 @@ class DBusException : Exception {
of its actual value. of its actual value.
+/ +/
class TypeMismatchException : Exception { class TypeMismatchException : Exception {
package this( package this(int expectedType, int actualType, string file = __FILE__,
int expectedType, size_t line = __LINE__, Throwable next = null) pure nothrow @safe {
int actualType,
string file = __FILE__,
size_t line = __LINE__,
Throwable next = null
) pure nothrow @safe {
string message; string message;
// dfmt off
if (expectedType == 'v') { if (expectedType == 'v') {
message = "The type of value at the current position in the message is" message = "The type of value at the current position in the message is"
~ " incompatible to the target variant type." ~ " incompatible to the target variant type." ~ " Type code of the value: '"
~ " Type code of the value: '" ~ cast(char) actualType ~ '\''; ~ cast(char) actualType ~ '\'';
} else { } else {
message = "The type of value at the current position in the message does" message = "The type of value at the current position in the message does"
~ " not match the type of value to be read." ~ " not match the type of value to be read." ~ " Expected: '"
~ " Expected: '" ~ cast(char) expectedType ~ "'," ~ cast(char) expectedType ~ "'," ~ " Got: '" ~ cast(char) actualType ~ '\'';
~ " Got: '" ~ cast(char) actualType ~ '\'';
} }
// dfmt on
this(message, expectedType, actualType, file, line, next); this(message, expectedType, actualType, file, line, next);
} }
this( this(string message, int expectedType, int actualType, string file = __FILE__,
string message, size_t line = __LINE__, Throwable next = null) pure nothrow @safe {
int expectedType,
int actualType,
string file = __FILE__,
size_t line = __LINE__,
Throwable next = null
) pure nothrow @safe {
_expectedType = expectedType; _expectedType = expectedType;
_actualType = actualType; _actualType = actualType;
super(message, file, line, next); super(message, file, line, next);
@ -98,19 +80,15 @@ class TypeMismatchException : Exception {
that have a constrained value set, such as Enums. that have a constrained value set, such as Enums.
+/ +/
class InvalidValueException : Exception { class InvalidValueException : Exception {
package this(Source)( package this(Source)(Source value, string targetType, string file = __FILE__,
Source value, size_t line = __LINE__, Throwable next = null) {
string targetType,
string file = __FILE__,
size_t line = __LINE__,
Throwable next = null
) {
import std.conv : to; import std.conv : to;
static if(__traits(compiles, value.to!string)) static if (__traits(compiles, value.to!string)) {
string valueString = value.to!string; string valueString = value.to!string;
else } else {
string valueString = "(unprintable)"; string valueString = "(unprintable)";
}
super("Value " ~ valueString ~ " cannot be represented in type " ~ targetType); super("Value " ~ valueString ~ " cannot be represented in type " ~ targetType);
} }

View file

@ -40,13 +40,14 @@ struct MessagePattern {
return hash; return hash;
} }
bool opEquals(ref const this s) const @safe pure nothrow { bool opEquals(ref const typeof(this) s) const @safe pure nothrow {
return (path == s.path) && (iface == s.iface) && (method == s.method) && (signal == s.signal); return (path == s.path) && (iface == s.iface) && (method == s.method) && (signal == s.signal);
} }
} }
unittest { unittest {
import dunit.toolkit; import dunit.toolkit;
auto msg = Message("org.example.test", "/test", "org.example.testing", "testMethod"); auto msg = Message("org.example.test", "/test", "org.example.testing", "testMethod");
auto patt = new MessagePattern(msg); auto patt = new MessagePattern(msg);
patt.assertEqual(patt); patt.assertEqual(patt);
@ -66,19 +67,23 @@ class MessageRouter {
bool handle(Message msg, Connection conn) { bool handle(Message msg, Connection conn) {
MessageType type = msg.type(); MessageType type = msg.type();
if(type != MessageType.Call && type != MessageType.Signal) if (type != MessageType.Call && type != MessageType.Signal) {
return false; return false;
}
auto pattern = MessagePattern(msg); auto pattern = MessagePattern(msg);
// import std.stdio; debug writeln("Handling ", pattern); // import std.stdio; debug writeln("Handling ", pattern);
if(pattern.iface == "org.freedesktop.DBus.Introspectable" && if (pattern.iface == "org.freedesktop.DBus.Introspectable"
pattern.method == "Introspect" && !pattern.signal) { && pattern.method == "Introspect" && !pattern.signal) {
handleIntrospect(pattern.path, msg, conn); handleIntrospect(pattern.path, msg, conn);
return true; return true;
} }
MessageHandler* handler = (pattern in callTable); MessageHandler* handler = (pattern in callTable);
if(handler is null) return false; if (handler is null) {
return false;
}
// Check for matching argument types // Check for matching argument types
version (DDBusNoChecking) { version (DDBusNoChecking) {
@ -97,25 +102,39 @@ class MessageRouter {
void handlerWrapper(Message call, Connection conn) { void handlerWrapper(Message call, Connection conn) {
Tuple!Args args = call.readTuple!(Tuple!Args)(); Tuple!Args args = call.readTuple!(Tuple!Args)();
auto retMsg = call.createReturn(); auto retMsg = call.createReturn();
static if (!is(Ret == void)) { static if (!is(Ret == void)) {
Ret ret = handler(args.expand); Ret ret = handler(args.expand);
static if (is(Ret == Tuple!T, T...)) static if (is(Ret == Tuple!T, T...)) {
retMsg.build!T(ret.expand); retMsg.build!T(ret.expand);
else } else {
retMsg.build(ret); retMsg.build(ret);
}
} else { } else {
handler(args.expand); handler(args.expand);
} }
if(!patt.signal)
if (!patt.signal) {
conn.send(retMsg); conn.send(retMsg);
} }
}
static string[] args = typeSigArr!Args; static string[] args = typeSigArr!Args;
static if (is(Ret == void)) { static if (is(Ret == void)) {
static string[] ret = []; static string[] ret = [];
} else { } else {
static string[] ret = typeSigReturn!Ret; static string[] ret = typeSigReturn!Ret;
} }
MessageHandler handleStruct = {func: &handlerWrapper, argSig: args, retSig: ret};
// dfmt off
MessageHandler handleStruct = {
func: &handlerWrapper,
argSig: args,
retSig: ret
};
// dfmt on
callTable[patt] = handleStruct; callTable[patt] = handleStruct;
} }
@ -123,25 +142,35 @@ class MessageRouter {
<node name="%s">`; <node name="%s">`;
string introspectXML(string path) { string introspectXML(string path) {
auto methods = callTable.byKey().filter!(a => (a.path == path) && !a.signal)().array() // dfmt off
// .schwartzSort!((a) => a.iface, "a<b")(); auto methods = callTable
.byKey()
.filter!(a => (a.path == path) && !a.signal)
.array
.sort!((a, b) => a.iface < b.iface)(); .sort!((a, b) => a.iface < b.iface)();
// dfmt on
auto ifaces = methods.groupBy(); auto ifaces = methods.groupBy();
auto app = appender!string; auto app = appender!string;
formattedWrite(app, introspectHeader, path); formattedWrite(app, introspectHeader, path);
foreach (iface; ifaces) { foreach (iface; ifaces) {
formattedWrite(app, `<interface name="%s">`, iface.front.iface); formattedWrite(app, `<interface name="%s">`, iface.front.iface);
foreach (methodPatt; iface.array()) { foreach (methodPatt; iface.array()) {
formattedWrite(app, `<method name="%s">`, methodPatt.method); formattedWrite(app, `<method name="%s">`, methodPatt.method);
auto handler = callTable[methodPatt]; auto handler = callTable[methodPatt];
foreach (arg; handler.argSig) { foreach (arg; handler.argSig) {
formattedWrite(app, `<arg type="%s" direction="in"/>`, arg); formattedWrite(app, `<arg type="%s" direction="in"/>`, arg);
} }
foreach (arg; handler.retSig) { foreach (arg; handler.retSig) {
formattedWrite(app, `<arg type="%s" direction="out"/>`, arg); formattedWrite(app, `<arg type="%s" direction="out"/>`, arg);
} }
app.put("</method>"); app.put("</method>");
} }
app.put("</interface>"); app.put("</interface>");
} }
@ -149,10 +178,12 @@ class MessageRouter {
if (!childPath.endsWith("/")) { if (!childPath.endsWith("/")) {
childPath ~= "/"; childPath ~= "/";
} }
auto children = callTable.byKey().filter!(a => (a.path.startsWith(childPath)) && !a.signal)()
.map!((s) => s.path.chompPrefix(childPath)) auto children = callTable.byKey()
.map!((s) => s.splitter('/').front) .filter!(a => (a.path.startsWith(childPath)) && !a.signal)().map!(
.array().sort().uniq(); (s) => s.path.chompPrefix(childPath)).map!((s) => s.splitter('/')
.front).array().sort().uniq();
foreach (child; children) { foreach (child; children) {
formattedWrite(app, `<node name="%s"/>`, child); formattedWrite(app, `<node name="%s"/>`, child);
} }
@ -168,13 +199,15 @@ class MessageRouter {
} }
} }
extern(C) private DBusHandlerResult filterFunc(DBusConnection *dConn, DBusMessage *dMsg, void *routerP) { extern (C) private DBusHandlerResult filterFunc(DBusConnection* dConn,
DBusMessage* dMsg, void* routerP) {
MessageRouter router = cast(MessageRouter) routerP; MessageRouter router = cast(MessageRouter) routerP;
dbus_message_ref(dMsg); dbus_message_ref(dMsg);
Message msg = Message(dMsg); Message msg = Message(dMsg);
dbus_connection_ref(dConn); dbus_connection_ref(dConn);
Connection conn = Connection(dConn); Connection conn = Connection(dConn);
bool handled = router.handle(msg, conn); bool handled = router.handle(msg, conn);
if (handled) { if (handled) {
return DBusHandlerResult.DBUS_HANDLER_RESULT_HANDLED; return DBusHandlerResult.DBUS_HANDLER_RESULT_HANDLED;
} else { } else {
@ -207,24 +240,46 @@ unittest{
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"); patt = MessagePattern("/root/bar", "ca.thume.tester", "lolwut");
router.setHandler!(Variant!DBusAny,int)(patt,(int p) {return variant(DBusAny(p));}); 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,Variant!DBusAny)(patt,(int p, Variant!DBusAny any) {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; });
patt = MessagePattern("/root/fancy", "ca.thume.tester", "crazyTest"); patt = MessagePattern("/root/fancy", "ca.thume.tester", "crazyTest");
enum F : ushort { a = 1, b = 8, c = 16 } enum F : ushort {
struct S { byte b; ulong ul; F f; } a = 1,
b = 8,
c = 16
}
struct S {
byte b;
ulong ul;
F f;
}
router.setHandler!(int)(patt, (Algebraic!(ushort, BitFlags!F, S) v) { router.setHandler!(int)(patt, (Algebraic!(ushort, BitFlags!F, S) v) {
if (v.type is typeid(ushort) || v.type is typeid(BitFlags!F)) { if (v.type is typeid(ushort) || v.type is typeid(BitFlags!F)) {
return v.coerce!int; return v.coerce!int;
} else if (v.type is typeid(S)) { } else if (v.type is typeid(S)) {
auto s = v.get!S; auto s = v.get!S;
final switch (s.f) { final switch (s.f) {
case F.a: return s.b; case F.a:
case F.b: return cast(int) s.ul; return s.b;
case F.c: return cast(int) s.ul + s.b; case F.b:
return cast(int) s.ul;
case F.c:
return cast(int) s.ul + s.b;
} }
} }
@ -246,5 +301,6 @@ unittest{
static string introspectResult3 = `<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> static string introspectResult3 = `<!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/fancy"><interface name="ca.thume.tester"><method name="crazyTest"><arg type="v" direction="in"/><arg type="i" direction="out"/></method></interface></node>`; <node name="/root/fancy"><interface name="ca.thume.tester"><method name="crazyTest"><arg type="v" direction="in"/><arg type="i" direction="out"/></method></interface></node>`;
router.introspectXML("/root/fancy").assertEqual(introspectResult3); router.introspectXML("/root/fancy").assertEqual(introspectResult3);
router.introspectXML("/").assertEndsWith(`<node name="/"><node name="root"/><node name="troll"/></node>`); router.introspectXML("/")
.assertEndsWith(`<node name="/"><node name="root"/><node name="troll"/></node>`);
} }

View file

@ -19,7 +19,8 @@ class PathIface {
this.iface = iface.toStringz(); this.iface = iface.toStringz();
} }
Ret call(Ret, Args...)(string meth, Args args) if(allCanDBus!Args && canDBus!Ret) { Ret call(Ret, Args...)(string meth, Args args)
if (allCanDBus!Args && canDBus!Ret) {
Message msg = Message(dbus_message_new_method_call(dest, path, iface, meth.toStringz())); Message msg = Message(dbus_message_new_method_call(dest, path, iface, meth.toStringz()));
msg.build(args); msg.build(args);
Message ret = conn.sendWithReplyBlocking(msg); Message ret = conn.sendWithReplyBlocking(msg);
@ -40,9 +41,10 @@ class PathIface {
unittest { unittest {
import dunit.toolkit; import dunit.toolkit;
Connection conn = connectToBus(); Connection conn = connectToBus();
PathIface obj = new PathIface(conn, "org.freedesktop.DBus","/org/freedesktop/DBus", PathIface obj = new PathIface(conn, "org.freedesktop.DBus",
"org.freedesktop.DBus"); "/org/freedesktop/DBus", "org.freedesktop.DBus");
auto names = obj.GetNameOwner("org.freedesktop.DBus").to!string(); auto names = obj.GetNameOwner("org.freedesktop.DBus").to!string();
names.assertEqual("org.freedesktop.DBus"); names.assertEqual("org.freedesktop.DBus");
obj.call!string("GetNameOwner", "org.freedesktop.DBus").assertEqual("org.freedesktop.DBus"); obj.call!string("GetNameOwner", "org.freedesktop.DBus").assertEqual("org.freedesktop.DBus");
@ -65,6 +67,7 @@ enum SignalMethod;
void registerMethods(T : Object)(MessageRouter router, string path, string iface, T obj) { void registerMethods(T : Object)(MessageRouter router, string path, string iface, T obj) {
MessagePattern patt = MessagePattern(path, iface, "", false); MessagePattern patt = MessagePattern(path, iface, "", false);
foreach (member; __traits(allMembers, T)) { foreach (member; __traits(allMembers, T)) {
// dfmt off
static if (__traits(compiles, __traits(getOverloads, obj, member)) static if (__traits(compiles, __traits(getOverloads, obj, member))
&& __traits(getOverloads, obj, member).length > 0 && __traits(getOverloads, obj, member).length > 0
&& __traits(compiles, router.setHandler(patt, &__traits(getOverloads, obj, member)[0]))) { && __traits(compiles, router.setHandler(patt, &__traits(getOverloads, obj, member)[0]))) {
@ -72,17 +75,25 @@ void registerMethods(T : Object)(MessageRouter router, string path, string iface
patt.signal = hasUDA!(__traits(getOverloads, obj, member)[0], SignalMethod); patt.signal = hasUDA!(__traits(getOverloads, obj, member)[0], SignalMethod);
router.setHandler(patt, &__traits(getOverloads, obj, member)[0]); router.setHandler(patt, &__traits(getOverloads, obj, member)[0]);
} }
// dfmt on
} }
} }
unittest { unittest {
import dunit.toolkit; import dunit.toolkit;
class Tester { class Tester {
int lol(int x, string s, string[string] map, Variant!DBusAny any) {return 5;} int lol(int x, string s, string[string] map, Variant!DBusAny any) {
void wat() {} return 5;
@SignalMethod
void signalRecv() {}
} }
void wat() {
}
@SignalMethod void signalRecv() {
}
}
auto o = new Tester; auto o = new Tester;
auto router = new MessageRouter; auto router = new MessageRouter;
registerMethods(router, "/", "ca.thume.test", o); registerMethods(router, "/", "ca.thume.test", o);

View file

@ -48,24 +48,28 @@ struct ObjectPath {
} }
ObjectPath opBinary(string op : "~")(string rhs) const pure @safe { ObjectPath opBinary(string op : "~")(string rhs) const pure @safe {
if (!rhs.startsWith("/")) if (!rhs.startsWith("/")) {
return opBinary!"~"(ObjectPath("/" ~ rhs)); return opBinary!"~"(ObjectPath("/" ~ rhs));
else } else {
return opBinary!"~"(ObjectPath(rhs)); return opBinary!"~"(ObjectPath(rhs));
} }
}
ObjectPath opBinary(string op : "~")(ObjectPath rhs) const pure @safe ObjectPath opBinary(string op : "~")(ObjectPath rhs) const pure @safe
in { in {
assert(ObjectPath.isValid(_value) && ObjectPath.isValid(rhs._value)); assert(ObjectPath.isValid(_value) && ObjectPath.isValid(rhs._value));
} out (v) { }
out (v) {
assert(ObjectPath.isValid(v._value)); assert(ObjectPath.isValid(v._value));
} body { }
body {
ObjectPath ret; ObjectPath ret;
if (_value == "/") if (_value == "/") {
ret._value = rhs._value; ret._value = rhs._value;
else } else {
ret._value = _value ~ rhs._value; ret._value = _value ~ rhs._value;
}
return ret; return ret;
} }
@ -85,20 +89,21 @@ struct ObjectPath {
static bool isValid(string objPath) pure @nogc nothrow @safe { static bool isValid(string objPath) pure @nogc nothrow @safe {
import std.ascii : isAlphaNum; import std.ascii : isAlphaNum;
if (!objPath.length) if (!objPath.length) {
return false; return false;
if (objPath == "/") }
if (objPath == "/") {
return true; return true;
if (objPath[0] != '/' || objPath[$ - 1] == '/') }
if (objPath[0] != '/' || objPath[$ - 1] == '/') {
return false; return false;
}
// .representation to avoid unicode exceptions -> @nogc & nothrow // .representation to avoid unicode exceptions -> @nogc & nothrow
return objPath.representation.splitter('/').drop(1) return objPath.representation.splitter('/').drop(1).all!(a => a.length
.all!(a => && a.all!(c => c.isAlphaNum || c == '_'));
a.length &&
a.all!(c =>
c.isAlphaNum || c == '_'
)
);
} }
} }
@ -137,8 +142,7 @@ struct DBusAny {
/// Same functionality as Variant!T but with dynamic types if true. /// Same functionality as Variant!T but with dynamic types if true.
bool explicitVariant; bool explicitVariant;
union union {
{
/// ///
byte int8; byte int8;
/// ///
@ -220,16 +224,18 @@ struct DBusAny {
signature = value.data.signature; signature = value.data.signature;
explicitVariant = true; explicitVariant = true;
if (type == 'a' || type == 'r') { if (type == 'a' || type == 'r') {
if(signature == ['y']) if (signature == ['y']) {
binaryData = value.data.binaryData; binaryData = value.data.binaryData;
else } else {
array = value.data.array; array = value.data.array;
} else if(type == 's') }
} else if (type == 's') {
str = value.data.str; str = value.data.str;
else if(type == 'e') } else if (type == 'e') {
entry = value.data.entry; entry = value.data.entry;
else } else {
uint64 = value.data.uint64; uint64 = value.data.uint64;
}
} else { } else {
this(value.data); this(value.data);
explicitVariant = true; explicitVariant = true;
@ -237,45 +243,61 @@ struct DBusAny {
} else static if (is(T : DictionaryEntry!(K, V), K, V)) { } else static if (is(T : DictionaryEntry!(K, V), K, V)) {
this('e', null, false); this('e', null, false);
entry = new DictionaryEntry!(DBusAny, DBusAny)(); entry = new DictionaryEntry!(DBusAny, DBusAny)();
static if(is(K == DBusAny)) static if (is(K == DBusAny)) {
entry.key = value.key; entry.key = value.key;
else } else {
entry.key = DBusAny(value.key); entry.key = DBusAny(value.key);
static if(is(V == DBusAny)) }
static if (is(V == DBusAny)) {
entry.value = value.value; entry.value = value.value;
else } else {
entry.value = DBusAny(value.value); entry.value = DBusAny(value.value);
}
} else static if (is(T == ubyte[]) || is(T == byte[])) { } else static if (is(T == ubyte[]) || is(T == byte[])) {
this('a', ['y'], false); this('a', ['y'], false);
binaryData = cast(ubyte[]) value; binaryData = cast(ubyte[]) value;
} else static if (isInputRange!T) { } else static if (isInputRange!T) {
this.type = 'a'; this.type = 'a';
static assert(!is(ElementType!T == DBusAny), "Array must consist of the same type, use Variant!DBusAny or DBusAny(tuple(...)) instead");
static assert(!is(ElementType!T == DBusAny),
"Array must consist of the same type, use Variant!DBusAny or DBusAny(tuple(...)) instead");
static assert(.typeSig!(ElementType!T) != "y"); static assert(.typeSig!(ElementType!T) != "y");
this.signature = .typeSig!(ElementType!T); this.signature = .typeSig!(ElementType!T);
this.explicitVariant = false; this.explicitVariant = false;
foreach(elem; value)
foreach (elem; value) {
array ~= DBusAny(elem); array ~= DBusAny(elem);
}
} else static if (isTuple!T) { } else static if (isTuple!T) {
this.type = 'r'; this.type = 'r';
this.signature = ['(']; this.signature = ['('];
this.explicitVariant = false; this.explicitVariant = false;
foreach (index, R; value.Types) { foreach (index, R; value.Types) {
auto var = DBusAny(value[index]); auto var = DBusAny(value[index]);
tuple ~= var; tuple ~= var;
if(var.explicitVariant)
if (var.explicitVariant) {
this.signature ~= 'v'; this.signature ~= 'v';
else { } else {
if (var.type != 'r') if (var.type != 'r') {
this.signature ~= cast(char) var.type; this.signature ~= cast(char) var.type;
if(var.type == 'a' || var.type == 'r') }
if (var.type == 'a' || var.type == 'r') {
this.signature ~= var.signature; this.signature ~= var.signature;
} }
} }
}
this.signature ~= ')'; this.signature ~= ')';
} else static if (isAssociativeArray!T) { } else static if (isAssociativeArray!T) {
this(value.byDictionaryEntries); this(value.byDictionaryEntries);
} else static assert(false, T.stringof ~ " not convertible to a Variant"); } else {
static assert(false, T.stringof ~ " not convertible to a Variant");
}
} }
/// ///
@ -318,10 +340,12 @@ struct DBusAny {
case 'a': case 'a':
import std.digest.digest : toHexString; import std.digest.digest : toHexString;
if(signature == ['y']) if (signature == ['y']) {
valueStr = "binary(" ~ binaryData.toHexString ~ ')'; valueStr = "binary(" ~ binaryData.toHexString ~ ')';
else } else {
valueStr = '[' ~ array.map!(a => a.toString).join(", ") ~ ']'; valueStr = '[' ~ array.map!(a => a.toString).join(", ") ~ ']';
}
break; break;
case 'r': case 'r':
valueStr = '(' ~ tuple.map!(a => a.toString).join(", ") ~ ')'; valueStr = '(' ~ tuple.map!(a => a.toString).join(", ") ~ ')';
@ -333,10 +357,9 @@ struct DBusAny {
valueStr = "unknown"; valueStr = "unknown";
break; break;
} }
return "DBusAny(" ~ cast(char) type
~ ", \"" ~ signature.idup return "DBusAny(" ~ cast(char) type ~ ", \"" ~ signature.idup ~ "\", " ~ (explicitVariant
~ "\", " ~ (explicitVariant ? "explicit" : "implicit") ? "explicit" : "implicit") ~ ", " ~ valueStr ~ ")";
~ ", " ~ valueStr ~ ")";
} }
/++ /++
@ -354,16 +377,13 @@ struct DBusAny {
DBusAny object is not the same as the DBus type used to represent T. DBusAny object is not the same as the DBus type used to represent T.
+/ +/
T get(T)() @property const T get(T)() @property const
if(staticIndexOf!(T, BasicTypes) >= 0) if (staticIndexOf!(T, BasicTypes) >= 0) {
{ enforce(type == typeCode!T, new TypeMismatchException(
enforce(type == typeCode!T, "Cannot get a " ~ T.stringof ~ " from a DBusAny with" ~ " a value of DBus type '" ~ typeSig ~ "'.",
new TypeMismatchException( typeCode!T, type));
"Cannot get a " ~ T.stringof ~ " from a DBusAny with"
~ " a value of DBus type '" ~ typeSig ~ "'.", typeCode!T, type));
static if (isIntegral!T) { static if (isIntegral!T) {
enum memberName = enum memberName = (isUnsigned!T ? "uint" : "int") ~ (T.sizeof * 8).to!string;
(isUnsigned!T ? "uint" : "int") ~ (T.sizeof * 8).to!string;
return __traits(getMember, this, memberName); return __traits(getMember, this, memberName);
} else static if (is(T == double)) { } else static if (is(T == double)) {
return float64; return float64;
@ -380,12 +400,9 @@ struct DBusAny {
/// ditto /// ditto
T get(T)() @property const T get(T)() @property const
if(is(T == const(DBusAny)[])) if (is(T == const(DBusAny)[])) {
{ enforce((type == 'a' && signature != "y") || type == 'r', new TypeMismatchException(
enforce((type == 'a' && signature != "y") || type == 'r', "Cannot get a " ~ T.stringof ~ " from a DBusAny with" ~ " a value of DBus type '" ~ this.typeSig ~ "'.",
new TypeMismatchException(
"Cannot get a " ~ T.stringof ~ " from a DBusAny with"
~ " a value of DBus type '" ~ this.typeSig ~ "'.",
typeCode!T, type)); typeCode!T, type));
return array; return array;
@ -393,12 +410,9 @@ struct DBusAny {
/// ditto /// ditto
T get(T)() @property const T get(T)() @property const
if (is(T == const(ubyte)[])) if (is(T == const(ubyte)[])) {
{ enforce(type == 'a' && signature == "y", new TypeMismatchException(
enforce(type == 'a' && signature == "y", "Cannot get a " ~ T.stringof ~ " from a DBusAny with" ~ " a value of DBus type '" ~ this.typeSig ~ "'.",
new TypeMismatchException(
"Cannot get a " ~ T.stringof ~ " from a DBusAny with"
~ " a value of DBus type '" ~ this.typeSig ~ "'.",
typeCode!T, type)); typeCode!T, type));
return binaryData; return binaryData;
@ -408,10 +422,12 @@ struct DBusAny {
DBusAny[DBusAny] toAA() { DBusAny[DBusAny] toAA() {
enforce(type == 'a' && signature && signature[0] == '{'); enforce(type == 'a' && signature && signature[0] == '{');
DBusAny[DBusAny] aa; DBusAny[DBusAny] aa;
foreach (val; array) { foreach (val; array) {
enforce(val.type == 'e'); enforce(val.type == 'e');
aa[val.entry.key] = val.entry.value; aa[val.entry.key] = val.entry.value;
} }
return aa; return aa;
} }
@ -421,8 +437,7 @@ struct DBusAny {
Returns: Returns:
The type signature of the value stored in this DBusAny object. The type signature of the value stored in this DBusAny object.
+/ +/
string typeSig() @property const pure nothrow @safe string typeSig() @property const pure nothrow @safe {
{
if (type == 'a') { if (type == 'a') {
return "a" ~ signature; return "a" ~ signature;
} else if (type == 'r') { } else if (type == 'r') {
@ -443,8 +458,9 @@ struct DBusAny {
auto v = to!R; auto v = to!R;
v.explicitVariant = false; v.explicitVariant = false;
return Variant!R(v); return Variant!R(v);
} else } else {
return Variant!R(to!R); return Variant!R(to!R);
}
} else static if (is(T == DBusAny)) { } else static if (is(T == DBusAny)) {
return this; return this;
} else static if (isIntegral!T || isFloatingPoint!T) { } else static if (isIntegral!T || isFloatingPoint!T) {
@ -469,78 +485,105 @@ struct DBusAny {
throw new Exception("Can't convert type " ~ cast(char) type ~ " to " ~ T.stringof); throw new Exception("Can't convert type " ~ cast(char) type ~ " to " ~ T.stringof);
} }
} else static if (is(T == bool)) { } else static if (is(T == bool)) {
if(type == 'b') if (type == 'b') {
return boolean; return boolean;
else } else {
throw new Exception("Can't convert type " ~ cast(char) type ~ " to " ~ T.stringof); throw new Exception("Can't convert type " ~ cast(char) type ~ " to " ~ T.stringof);
}
} else static if (isSomeString!T) { } else static if (isSomeString!T) {
if(type == 's') if (type == 's') {
return str.to!T; return str.to!T;
else if(type == 'o') } else if (type == 'o') {
return obj.toString(); return obj.toString();
else } else {
throw new Exception("Can't convert type " ~ cast(char) type ~ " to " ~ T.stringof); throw new Exception("Can't convert type " ~ cast(char) type ~ " to " ~ T.stringof);
}
} else static if (is(T == ObjectPath)) { } else static if (is(T == ObjectPath)) {
if(type == 'o') if (type == 'o') {
return obj; return obj;
else } else {
throw new Exception("Can't convert type " ~ cast(char) type ~ " to " ~ T.stringof); throw new Exception("Can't convert type " ~ cast(char) type ~ " to " ~ T.stringof);
}
} else static if (isDynamicArray!T) { } else static if (isDynamicArray!T) {
if(type != 'a' && type != 'r') if (type != 'a' && type != 'r') {
throw new Exception("Can't convert type " ~ cast(char) type ~ " to an array"); throw new Exception("Can't convert type " ~ cast(char) type ~ " to an array");
}
T ret; T ret;
if (signature == ['y']) { if (signature == ['y']) {
static if(isIntegral!(ElementType!T)) static if (isIntegral!(ElementType!T)) {
foreach(elem; binaryData) foreach (elem; binaryData) {
ret ~= elem.to!(ElementType!T); ret ~= elem.to!(ElementType!T);
} else }
foreach(elem; array) }
} else {
foreach (elem; array) {
ret ~= elem.to!(ElementType!T); ret ~= elem.to!(ElementType!T);
}
}
return ret; return ret;
} else static if (isTuple!T) { } else static if (isTuple!T) {
if(type != 'r') if (type != 'r') {
throw new Exception("Can't convert type " ~ cast(char) type ~ " to " ~ T.stringof); throw new Exception("Can't convert type " ~ cast(char) type ~ " to " ~ T.stringof);
}
T ret; T ret;
enforce(ret.Types.length == tuple.length, "Tuple length mismatch"); enforce(ret.Types.length == tuple.length, "Tuple length mismatch");
foreach(index, T; ret.Types) foreach (index, T; ret.Types) {
ret[index] = tuple[index].to!T; ret[index] = tuple[index].to!T;
}
return ret; return ret;
} else static if (isAssociativeArray!T) { } else static if (isAssociativeArray!T) {
if(type != 'a' || !signature || signature[0] != '{') if (type != 'a' || !signature || signature[0] != '{') {
throw new Exception("Can't convert type " ~ cast(char) type ~ " to " ~ T.stringof); throw new Exception("Can't convert type " ~ cast(char) type ~ " to " ~ T.stringof);
}
T ret; T ret;
foreach (pair; array) { foreach (pair; array) {
enforce(pair.type == 'e'); enforce(pair.type == 'e');
ret[pair.entry.key.to!(KeyType!T)] = pair.entry.value.to!(ValueType!T); ret[pair.entry.key.to!(KeyType!T)] = pair.entry.value.to!(ValueType!T);
} }
return ret; return ret;
} else static assert(false, "Can't convert variant to " ~ T.stringof); } else {
static assert(false, "Can't convert variant to " ~ T.stringof);
}
} }
bool opEquals(ref in DBusAny b) const { bool opEquals(ref in DBusAny b) const {
if(b.type != type || b.explicitVariant != explicitVariant) if (b.type != type || b.explicitVariant != explicitVariant) {
return false; return false;
if((type == 'a' || type == 'r') && b.signature != signature) }
if ((type == 'a' || type == 'r') && b.signature != signature) {
return false; return false;
if(type == 'a' && signature == ['y']) }
if (type == 'a' && signature == ['y']) {
return binaryData == b.binaryData; return binaryData == b.binaryData;
if(type == 'a') }
if (type == 'a') {
return array == b.array; return array == b.array;
else if(type == 'r') } else if (type == 'r') {
return tuple == b.tuple; return tuple == b.tuple;
else if(type == 's') } else if (type == 's') {
return str == b.str; return str == b.str;
else if(type == 'o') } else if (type == 'o') {
return obj == b.obj; return obj == b.obj;
else if(type == 'e') } else if (type == 'e') {
return entry == b.entry || (entry && b.entry && *entry == *b.entry); return entry == b.entry || (entry && b.entry && *entry == *b.entry);
else } else {
return uint64 == b.uint64; return uint64 == b.uint64;
} }
} }
}
unittest { unittest {
import dunit.toolkit; import dunit.toolkit;
DBusAny set(string member, T)(DBusAny v, T value) { DBusAny set(string member, T)(DBusAny v, T value) {
mixin("v." ~ member ~ " = value;"); mixin("v." ~ member ~ " = value;");
return v; return v;
@ -550,12 +593,14 @@ unittest {
assertEqual(DBusAny(value), b); assertEqual(DBusAny(value), b);
static if (is(T == Variant!R, R)) { static if (is(T == Variant!R, R)) {
static if(__traits(compiles, b.get!R)) static if (__traits(compiles, b.get!R)) {
assertEqual(b.get!R, value.data); assertEqual(b.get!R, value.data);
}
} else { } else {
static if(__traits(compiles, b.get!T)) static if (__traits(compiles, b.get!T)) {
assertEqual(b.get!T, value); assertEqual(b.get!T, value);
} }
}
assertEqual(b.to!T, value); assertEqual(b.to!T, value);
b.toString(); b.toString();
@ -569,7 +614,8 @@ unittest {
test(cast(long) 184, set!"int64"(DBusAny('x', null, false), cast(long) 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(cast(ulong) 184, set!"uint64"(DBusAny('t', null, false), cast(ulong) 184));
test(true, set!"boolean"(DBusAny('b', null, false), true)); test(true, set!"boolean"(DBusAny('b', null, false), true));
test(cast(ubyte[]) [1, 2, 3], set!"binaryData"(DBusAny('a', ['y'], false), cast(ubyte[]) [1, 2, 3])); test(cast(ubyte[])[1, 2, 3], set!"binaryData"(DBusAny('a', ['y'], false),
cast(ubyte[])[1, 2, 3]));
test(variant(cast(ubyte) 184), set!"int8"(DBusAny('y', null, true), cast(byte) 184)); 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(short) 184), set!"int16"(DBusAny('n', null, true), cast(short) 184));
@ -579,18 +625,24 @@ unittest {
test(variant(cast(long) 184), set!"int64"(DBusAny('x', null, true), cast(long) 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(cast(ulong) 184), set!"uint64"(DBusAny('t', null, true), cast(ulong) 184));
test(variant(true), set!"boolean"(DBusAny('b', null, true), true)); test(variant(true), set!"boolean"(DBusAny('b', null, true), true));
test(variant(cast(ubyte[]) [1, 2, 3]), set!"binaryData"(DBusAny('a', ['y'], true), cast(ubyte[]) [1, 2, 3])); test(variant(cast(ubyte[])[1, 2, 3]), set!"binaryData"(DBusAny('a', ['y'],
true), cast(ubyte[])[1, 2, 3]));
test(variant(DBusAny(5)), set!"int32"(DBusAny('i', null, true), 5)); 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([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(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", 4, [1, 2]), set!"tuple"(DBusAny('r', "(siai)".dup, false),
test(tuple("a", variant(4), variant([1, 2])), set!"tuple"(DBusAny('r', "(svv)", false), [DBusAny("a"), DBusAny(variant(4)), DBusAny(variant([1, 2]))])); [DBusAny("a"), DBusAny(4), DBusAny([1, 2])]));
test(tuple("a", variant(4), variant([1, 2])), set!"tuple"(DBusAny('r',
"(svv)", false), [DBusAny("a"), DBusAny(variant(4)), DBusAny(variant([1, 2]))]));
test(["a": "b"], set!"array"(DBusAny('a', "{ss}", false), [DBusAny(DictionaryEntry!(DBusAny, DBusAny)(DBusAny("a"), DBusAny("b")))])); test(["a" : "b"], set!"array"(DBusAny('a', "{ss}", false),
test([variant("a"): 4], set!"array"(DBusAny('a', "{vi}", false), [DBusAny(DictionaryEntry!(DBusAny, DBusAny)(DBusAny(variant("a")), DBusAny(4)))])); [DBusAny(DictionaryEntry!(DBusAny, DBusAny)(DBusAny("a"), DBusAny("b")))]));
test([variant("a") : 4], set!"array"(DBusAny('a', "{vi}", false),
[DBusAny(DictionaryEntry!(DBusAny, DBusAny)(DBusAny(variant("a")), DBusAny(4)))]));
} }
/// Marks the data as variant on serialization /// Marks the data as variant on serialization
@ -605,14 +657,18 @@ Variant!T variant(T)(T data) {
enum MessageType { enum MessageType {
Invalid = 0, Invalid = 0,
Call, Return, Error, Signal Call,
Return,
Error,
Signal
} }
struct Message { struct Message {
DBusMessage* msg; DBusMessage* msg;
this(string dest, string path, string iface, string method) { this(string dest, string path, string iface, string method) {
msg = dbus_message_new_method_call(dest.toStringz(), path.toStringz(), iface.toStringz(), method.toStringz()); msg = dbus_message_new_method_call(dest.toStringz(), path.toStringz(),
iface.toStringz(), method.toStringz());
} }
this(DBusMessage* m) { this(DBusMessage* m) {
@ -627,7 +683,8 @@ struct Message {
dbus_message_unref(msg); dbus_message_unref(msg);
} }
void build(TS...)(TS args) if(allCanDBus!TS) { void build(TS...)(TS args)
if (allCanDBus!TS) {
DBusMessageIter iter; DBusMessageIter iter;
dbus_message_iter_init_append(msg, &iter); dbus_message_iter_init_append(msg, &iter);
buildIter(&iter, args); buildIter(&iter, args);
@ -639,14 +696,17 @@ struct Message {
read the first argument. This is suitable for single item returns. read the first argument. This is suitable for single item returns.
To read multiple arguments use readTuple. To read multiple arguments use readTuple.
*/ */
T read(T)() if(canDBus!T) { T read(T)()
if (canDBus!T) {
DBusMessageIter iter; DBusMessageIter iter;
dbus_message_iter_init(msg, &iter); dbus_message_iter_init(msg, &iter);
return readIter!T(&iter); return readIter!T(&iter);
} }
alias read to; alias read to;
Tup readTuple(Tup)() if(isTuple!Tup && allCanDBus!(Tup.Types)) { Tup readTuple(Tup)()
if (isTuple!Tup && allCanDBus!(Tup.Types)) {
DBusMessageIter iter; DBusMessageIter iter;
dbus_message_iter_init(msg, &iter); dbus_message_iter_init(msg, &iter);
Tup ret; Tup ret;
@ -673,21 +733,25 @@ struct Message {
assert(cStr != null); assert(cStr != null);
return cStr.fromStringz().idup; return cStr.fromStringz().idup;
} }
string path() { string path() {
const(char)* cStr = dbus_message_get_path(msg); const(char)* cStr = dbus_message_get_path(msg);
assert(cStr != null); assert(cStr != null);
return cStr.fromStringz().idup; return cStr.fromStringz().idup;
} }
string iface() { string iface() {
const(char)* cStr = dbus_message_get_interface(msg); const(char)* cStr = dbus_message_get_interface(msg);
assert(cStr != null); assert(cStr != null);
return cStr.fromStringz().idup; return cStr.fromStringz().idup;
} }
string member() { string member() {
const(char)* cStr = dbus_message_get_member(msg); const(char)* cStr = dbus_message_get_member(msg);
assert(cStr != null); assert(cStr != null);
return cStr.fromStringz().idup; return cStr.fromStringz().idup;
} }
string sender() { string sender() {
const(char)* cStr = dbus_message_get_sender(msg); const(char)* cStr = dbus_message_get_sender(msg);
assert(cStr != null); assert(cStr != null);
@ -697,6 +761,7 @@ struct Message {
unittest { unittest {
import dunit.toolkit; import dunit.toolkit;
auto msg = Message("org.example.test", "/test", "org.example.testing", "testMethod"); auto msg = Message("org.example.test", "/test", "org.example.testing", "testMethod");
msg.path().assertEqual("/test"); msg.path().assertEqual("/test");
} }
@ -774,26 +839,17 @@ unittest {
__gshared int dummy; __gshared int dummy;
enum testStruct = S3( enum testStruct = S3(variant(5), "blah", S1(-7, 63.5, "test"), S2(84, -123,
variant(5), "blah", 78, 432, &dummy), 16);
S1(-7, 63.5, "test"),
S2(84, -123, 78, 432, &dummy),
16
);
msg.build(testStruct); msg.build(testStruct);
// Non-marshaled fields should appear as freshly initialized // Non-marshaled fields should appear as freshly initialized
enum expectedResult = S3( enum expectedResult = S3(variant(5), "blah", S1(int.init, 63.5, "test"),
variant(5), "blah", S2(int.init, int.init, 78, 432, null), uint.init);
S1(int.init, 63.5, "test"),
S2(int.init, int.init, 78, 432, null),
uint.init
);
msg.read!S3().assertEqual(expectedResult); msg.read!S3().assertEqual(expectedResult);
} }
Connection connectToBus(DBusBusType bus = DBusBusType.DBUS_BUS_SESSION) { Connection connectToBus(DBusBusType bus = DBusBusType.DBUS_BUS_SESSION) {
@ -803,6 +859,7 @@ Connection connectToBus(DBusBusType bus = DBusBusType.DBUS_BUS_SESSION) {
unittest { unittest {
import dunit.toolkit; import dunit.toolkit;
// This test will only pass if DBus is installed. // This test will only pass if DBus is installed.
Connection conn = connectToBus(); Connection conn = connectToBus();
conn.conn.assertTruthy(); conn.conn.assertTruthy();

View file

@ -26,12 +26,10 @@ auto byDictionaryEntries(K, V)(V[K] aa) {
Its meaning became unclear when support for Phobos-style variants was added. Its meaning became unclear when support for Phobos-style variants was added.
It seemed best to remove it at that point. It seemed best to remove it at that point.
+/ +/
deprecated("Use std.traits.isInstanceOf instead.") deprecated("Use std.traits.isInstanceOf instead.") template isVariant(T) {
template isVariant(T) {
static if (isBasicType!T || isInputRange!T) { static if (isBasicType!T || isInputRange!T) {
enum isVariant = false; enum isVariant = false;
} else static if(__traits(compiles, TemplateOf!T) } else static if (__traits(compiles, TemplateOf!T) && __traits(isSame, TemplateOf!T, Variant)) {
&& __traits(isSame, TemplateOf!T, Variant)) {
enum isVariant = true; enum isVariant = true;
} else { } else {
enum isVariant = false; enum isVariant = false;
@ -56,19 +54,8 @@ template allCanDBus(TS...) {
AliasSeq of all basic types in terms of the DBus typesystem AliasSeq of all basic types in terms of the DBus typesystem
+/ +/
package // Don't add to the API yet, 'cause I intend to move it later package // Don't add to the API yet, 'cause I intend to move it later
alias BasicTypes = AliasSeq!( alias BasicTypes = AliasSeq!(bool, byte, short, ushort, int, uint, long, ulong,
bool, double, string, ObjectPath);
byte,
short,
ushort,
int,
uint,
long,
ulong,
double,
string,
ObjectPath
);
template basicDBus(T) { template basicDBus(T) {
static if (staticIndexOf!(T, BasicTypes) >= 0) { static if (staticIndexOf!(T, BasicTypes) >= 0) {
@ -110,6 +97,7 @@ template canDBus(T) {
unittest { unittest {
import dunit.toolkit; import dunit.toolkit;
(canDBus!int).assertTrue(); (canDBus!int).assertTrue();
(canDBus!(int[])).assertTrue(); (canDBus!(int[])).assertTrue();
(allCanDBus!(int, string, bool)).assertTrue(); (allCanDBus!(int, string, bool)).assertTrue();
@ -118,7 +106,8 @@ unittest {
(canDBus!(int[string])).assertTrue(); (canDBus!(int[string])).assertTrue();
} }
string typeSig(T)() if(canDBus!T) { string typeSig(T)()
if (canDBus!T) {
static if (is(T == byte)) { static if (is(T == byte)) {
return "y"; return "y";
} else static if (is(T == bool)) { } else static if (is(T == bool)) {
@ -149,7 +138,8 @@ string typeSig(T)() if(canDBus!T) {
alias TemplateArgsOf!T[0] E; alias TemplateArgsOf!T[0] E;
return typeSig!E; return typeSig!E;
} else static if (is(T == DBusAny)) { } else static if (is(T == DBusAny)) {
static assert(false, "Cannot determine type signature of DBusAny. Change to Variant!DBusAny if a variant was desired."); 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) {
@ -171,20 +161,23 @@ string typeSig(T)() if(canDBus!T) {
} }
} }
string typeSig(T)() if(isInstanceOf!(DictionaryEntry, T)) { string typeSig(T)()
if (isInstanceOf!(DictionaryEntry, T)) {
alias typeof(T.key) K; alias typeof(T.key) K;
alias typeof(T.value) V; alias typeof(T.value) V;
return "{" ~ typeSig!K ~ typeSig!V ~ '}'; return "{" ~ typeSig!K ~ typeSig!V ~ '}';
} }
string[] typeSigReturn(T)() if(canDBus!T) { string[] typeSigReturn(T)()
if (canDBus!T) {
static if (is(T == Tuple!TS, TS...)) static if (is(T == Tuple!TS, TS...))
return typeSigArr!TS; return typeSigArr!TS;
else else
return [typeSig!T]; return [typeSig!T];
} }
string typeSigAll(TS...)() if(allCanDBus!TS) { string typeSigAll(TS...)()
if (allCanDBus!TS) {
string sig = ""; string sig = "";
foreach (i, T; TS) { foreach (i, T; TS) {
sig ~= typeSig!T(); sig ~= typeSig!T();
@ -192,7 +185,8 @@ string typeSigAll(TS...)() if(allCanDBus!TS) {
return sig; return sig;
} }
string[] typeSigArr(TS...)() if(allCanDBus!TS) { string[] typeSigArr(TS...)()
if (allCanDBus!TS) {
string[] sig = []; string[] sig = [];
foreach (i, T; TS) { foreach (i, T; TS) {
sig ~= typeSig!T(); sig ~= typeSig!T();
@ -200,37 +194,66 @@ string[] typeSigArr(TS...)() if(allCanDBus!TS) {
return sig; return sig;
} }
int typeCode(T)() if(canDBus!T) { int typeCode(T)()
if (canDBus!T) {
int code = typeSig!T()[0]; int code = typeSig!T()[0];
return (code != '(') ? code : 'r'; return (code != '(') ? code : 'r';
} }
int typeCode(T)() if(isInstanceOf!(DictionaryEntry, T) && canDBus!(T[])) { int typeCode(T)()
if (isInstanceOf!(DictionaryEntry, T) && canDBus!(T[])) {
return 'e'; return 'e';
} }
unittest { unittest {
import dunit.toolkit; import dunit.toolkit;
// basics // basics
typeSig!int().assertEqual("i"); typeSig!int().assertEqual("i");
typeSig!bool().assertEqual("b"); typeSig!bool().assertEqual("b");
typeSig!string().assertEqual("s"); typeSig!string().assertEqual("s");
typeSig!(Variant!int)().assertEqual("v"); typeSig!(Variant!int)().assertEqual("v");
// enums // enums
enum E : byte { a, b, c } enum E : byte {
a,
b,
c
}
typeSig!E().assertEqual(typeSig!byte()); typeSig!E().assertEqual(typeSig!byte());
enum U : string { One = "One", Two = "Two" } enum U : string {
One = "One",
Two = "Two"
}
typeSig!U().assertEqual(typeSig!string()); typeSig!U().assertEqual(typeSig!string());
// bit flags // bit flags
enum F : uint { a = 1, b = 2, c = 4 } enum F : uint {
a = 1,
b = 2,
c = 4
}
typeSig!(BitFlags!F)().assertEqual(typeSig!uint()); typeSig!(BitFlags!F)().assertEqual(typeSig!uint());
// tuples (represented as structs in DBus) // tuples (represented as structs in DBus)
typeSig!(Tuple!(int, string, string)).assertEqual("(iss)"); typeSig!(Tuple!(int, string, string)).assertEqual("(iss)");
typeSig!(Tuple!(int,string,Variant!int,Tuple!(int,"k",double,"x"))).assertEqual("(isv(id))"); typeSig!(Tuple!(int, string, Variant!int, Tuple!(int, "k", double, "x"))).assertEqual(
"(isv(id))");
// structs // structs
struct S1 { int a; double b; string s; } struct S1 {
int a;
double b;
string s;
}
typeSig!S1.assertEqual("(ids)"); typeSig!S1.assertEqual("(ids)");
struct S2 { Variant!int c; string d; S1 e; uint f; } struct S2 {
Variant!int c;
string d;
S1 e;
uint f;
}
typeSig!S2.assertEqual("(vs(ids)u)"); typeSig!S2.assertEqual("(vs(ids)u)");
// arrays // arrays
typeSig!(int[]).assertEqual("ai"); typeSig!(int[]).assertEqual("ai");
@ -257,11 +280,12 @@ unittest {
sig3.assertEqual("iss"); sig3.assertEqual("iss");
} }
private template AllowedFieldTypes(S) if (is(S == struct)) { private template AllowedFieldTypes(S)
if (is(S == struct)) {
import ddbus.attributes : isAllowedField; import ddbus.attributes : isAllowedField;
import std.meta : Filter, staticMap; import std.meta : Filter, staticMap;
static alias TypeOf(alias sym) = typeof(sym); static alias TypeOf(alias sym) = typeof(sym);
alias AllowedFieldTypes = alias AllowedFieldTypes = staticMap!(TypeOf, Filter!(isAllowedField, S.tupleof));
staticMap!(TypeOf, Filter!(isAllowedField, S.tupleof));
} }