Merge pull request #21 from thaven/feature/struct
Support for D struct types
This commit is contained in:
commit
a737496d37
|
@ -5,6 +5,7 @@ import ddbus.exception : InvalidValueException, TypeMismatchException;
|
||||||
import ddbus.util;
|
import ddbus.util;
|
||||||
import ddbus.thin;
|
import ddbus.thin;
|
||||||
import std.exception : enforce;
|
import std.exception : enforce;
|
||||||
|
import std.meta: allSatisfy;
|
||||||
import std.string;
|
import std.string;
|
||||||
import std.typecons;
|
import std.typecons;
|
||||||
import std.range;
|
import std.range;
|
||||||
|
@ -112,6 +113,26 @@ void buildIter(TS...)(DBusMessageIter *iter, TS args) if(allCanDBus!TS) {
|
||||||
const(char)* subSig = typeSig!(VariantType!T).toStringz();
|
const(char)* subSig = typeSig!(VariantType!T).toStringz();
|
||||||
dbus_message_iter_open_container(iter, 'v', subSig, &sub);
|
dbus_message_iter_open_container(iter, 'v', subSig, &sub);
|
||||||
buildIter(&sub, arg.data);
|
buildIter(&sub, arg.data);
|
||||||
|
dbus_message_iter_close_container(iter, &sub);
|
||||||
|
} else static if(is(T == struct)) {
|
||||||
|
DBusMessageIter sub;
|
||||||
|
dbus_message_iter_open_container(iter, 'r', null, &sub);
|
||||||
|
|
||||||
|
// Following failed because of missing 'this' for members of arg.
|
||||||
|
// That sucks. It worked without Filter.
|
||||||
|
// Reported: https://issues.dlang.org/show_bug.cgi?id=17692
|
||||||
|
// buildIter(&sub, Filter!(isAllowedField, arg.tupleof));
|
||||||
|
|
||||||
|
// Using foreach to work around the issue
|
||||||
|
foreach(i, member; arg.tupleof) {
|
||||||
|
// Ugly, but we need to use tupleof again in the condition, because when
|
||||||
|
// we use `member`, isAllowedField will fail because it'll find this
|
||||||
|
// nice `buildIter` function instead of T when it looks up the parent
|
||||||
|
// scope of its argument.
|
||||||
|
static if (isAllowedField!(arg.tupleof[i]))
|
||||||
|
buildIter(&sub, member);
|
||||||
|
}
|
||||||
|
|
||||||
dbus_message_iter_close_container(iter, &sub);
|
dbus_message_iter_close_container(iter, &sub);
|
||||||
} else static if(basicDBus!T) {
|
} else static if(basicDBus!T) {
|
||||||
dbus_message_iter_append_basic(iter,typeCode!T,&arg);
|
dbus_message_iter_append_basic(iter,typeCode!T,&arg);
|
||||||
|
@ -265,6 +286,10 @@ T readIter(T)(DBusMessageIter *iter) if (!is(T == enum) && !isInstanceOf!(BitFla
|
||||||
ret.entry.key = readIter!DBusAny(&sub);
|
ret.entry.key = readIter!DBusAny(&sub);
|
||||||
ret.entry.value = readIter!DBusAny(&sub);
|
ret.entry.value = readIter!DBusAny(&sub);
|
||||||
}
|
}
|
||||||
|
} else static if(is(T == struct)) {
|
||||||
|
DBusMessageIter sub;
|
||||||
|
dbus_message_iter_recurse(iter, &sub);
|
||||||
|
readIterStruct!T(&sub, ret);
|
||||||
} else static if(basicDBus!T) {
|
} else static if(basicDBus!T) {
|
||||||
dbus_message_iter_get_basic(iter, &ret);
|
dbus_message_iter_get_basic(iter, &ret);
|
||||||
}
|
}
|
||||||
|
@ -279,6 +304,73 @@ void readIterTuple(Tup)(DBusMessageIter *iter, ref Tup tuple) if(isTuple!Tup &&
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void readIterStruct(S)(DBusMessageIter *iter, ref S s) if(is(S == struct) && allCanDBus!(Fields!S)) {
|
||||||
|
foreach(index, T; Fields!S) {
|
||||||
|
static if (isAllowedField!(s.tupleof[index])) {
|
||||||
|
s.tupleof[index] = readIter!T(iter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/++
|
||||||
|
Flags for use with dbusMarshaling UDA
|
||||||
|
|
||||||
|
Default is to include public fields only
|
||||||
|
+/
|
||||||
|
enum MarshalingFlag : ubyte {
|
||||||
|
includePrivateFields = 1 << 0, /// Automatically include private fields
|
||||||
|
manualOnly = 1 << 7 /// Only include fields with explicit
|
||||||
|
/// `@Yes.DBusMarshal`. This overrides any
|
||||||
|
/// `include` flags.
|
||||||
|
}
|
||||||
|
|
||||||
|
private template isMarshalingFlag(T) {
|
||||||
|
enum isMarshalingFlag = is(T == MarshalingFlag);
|
||||||
|
}
|
||||||
|
|
||||||
|
/++
|
||||||
|
UDA for specifying DBus marshaling options on structs
|
||||||
|
+/
|
||||||
|
auto dbusMarshaling(Args)(Args args ...)
|
||||||
|
if (allSatisfy!(isMarshalingFlag, Args)) {
|
||||||
|
return BitFlags!MarshalingFlag(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
private template marshalingFlags(S) if (is(S == struct)) {
|
||||||
|
private alias getUDAs!(S, BitFlags!MarshalingFlag) UDAs;
|
||||||
|
|
||||||
|
static if (UDAs.length == 0)
|
||||||
|
enum marshalingFlags = BitFlags!MarshalingFlag.init;
|
||||||
|
else {
|
||||||
|
static assert (UDAs.length == 1,
|
||||||
|
"Only one @dbusMarshaling UDA allowed on type.");
|
||||||
|
static assert (is(typeof(UDAs[0]) == BitFlags!MarshalingFlag),
|
||||||
|
"Huh? Did you intend to use @dbusMarshaling UDA?");
|
||||||
|
enum marshalingFlags = UDAs[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private template isAllowedField(alias field) {
|
||||||
|
private enum flags = marshalingFlags!(__traits(parent, field));
|
||||||
|
private alias getUDAs!(field, Flag!"DBusMarshal") UDAs;
|
||||||
|
|
||||||
|
static if (UDAs.length != 0) {
|
||||||
|
static assert (UDAs.length == 1,
|
||||||
|
"Only one UDA of type Flag!\"DBusMarshal\" allowed on struct field.");
|
||||||
|
static assert (is(typeof(UDAs[0]) == Flag!"DBusMarshal"),
|
||||||
|
"Did you intend to add UDA Yes.DBusMarshal or No.DBusMarshal?");
|
||||||
|
enum isAllowedField = cast(bool) UDAs[0];
|
||||||
|
} else static if (!(flags & MarshalingFlag.manualOnly)) {
|
||||||
|
static if (__traits(getProtection, field) == "public")
|
||||||
|
enum isAllowedField = true;
|
||||||
|
else static if (cast(bool) (flags & MarshalingFlag.includePrivateFields))
|
||||||
|
enum isAllowedField = true;
|
||||||
|
else
|
||||||
|
enum isAllowedField = false;
|
||||||
|
} else
|
||||||
|
enum isAllowedField = false;
|
||||||
|
}
|
||||||
|
|
||||||
unittest {
|
unittest {
|
||||||
import dunit.toolkit;
|
import dunit.toolkit;
|
||||||
import ddbus.thin;
|
import ddbus.thin;
|
||||||
|
@ -355,3 +447,4 @@ unittest {
|
||||||
readIter!F(&iter).assertThrow!InvalidValueException();
|
readIter!F(&iter).assertThrow!InvalidValueException();
|
||||||
readIter!(BitFlags!F)(&iter2).assertThrow!InvalidValueException();
|
readIter!(BitFlags!F)(&iter2).assertThrow!InvalidValueException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -570,6 +570,54 @@ struct Connection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unittest {
|
||||||
|
import dunit.toolkit;
|
||||||
|
|
||||||
|
struct S1 {
|
||||||
|
private int a;
|
||||||
|
private @(Yes.DBusMarshal) double b;
|
||||||
|
string s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@dbusMarshaling(MarshalingFlag.manualOnly)
|
||||||
|
struct S2 {
|
||||||
|
int h, i;
|
||||||
|
@(Yes.DBusMarshal) int j, k;
|
||||||
|
}
|
||||||
|
|
||||||
|
@dbusMarshaling(MarshalingFlag.includePrivateFields)
|
||||||
|
struct S3 {
|
||||||
|
private Variant!int c;
|
||||||
|
string d;
|
||||||
|
S1 e;
|
||||||
|
S2 f;
|
||||||
|
@(No.DBusMarshal) uint g;
|
||||||
|
}
|
||||||
|
|
||||||
|
Message msg = Message("org.example.wow", "/wut", "org.test.iface", "meth3");
|
||||||
|
|
||||||
|
enum testStruct = S3(
|
||||||
|
variant(5), "blah",
|
||||||
|
S1(-7, 63.5, "test"),
|
||||||
|
S2(84, -123, 78, 432),
|
||||||
|
16
|
||||||
|
);
|
||||||
|
|
||||||
|
msg.build(testStruct);
|
||||||
|
|
||||||
|
// Non-marshaled fields should appear as freshly initialized
|
||||||
|
enum expectedResult = S3(
|
||||||
|
variant(5), "blah",
|
||||||
|
S1(int.init, 63.5, "test"),
|
||||||
|
S2(int.init, int.init, 78, 432),
|
||||||
|
uint.init
|
||||||
|
);
|
||||||
|
|
||||||
|
msg.read!S3().assertEqual(expectedResult);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
Connection connectToBus(DBusBusType bus = DBusBusType.DBUS_BUS_SESSION) {
|
Connection connectToBus(DBusBusType bus = DBusBusType.DBUS_BUS_SESSION) {
|
||||||
DBusConnection *conn = wrapErrors((err) { return dbus_bus_get(bus,err); });
|
DBusConnection *conn = wrapErrors((err) { return dbus_bus_get(bus,err); });
|
||||||
return Connection(conn);
|
return Connection(conn);
|
||||||
|
|
|
@ -72,6 +72,8 @@ template canDBus(T) {
|
||||||
}
|
}
|
||||||
} else static if(isAssociativeArray!T) {
|
} else static if(isAssociativeArray!T) {
|
||||||
enum canDBus = basicDBus!(KeyType!T) && canDBus!(ValueType!T);
|
enum canDBus = basicDBus!(KeyType!T) && canDBus!(ValueType!T);
|
||||||
|
} else static if(is(T == struct) && !isInstanceOf!(DictionaryEntry, T)) {
|
||||||
|
enum canDBus = allCanDBus!(Fields!T);
|
||||||
} else {
|
} else {
|
||||||
enum canDBus = false;
|
enum canDBus = false;
|
||||||
}
|
}
|
||||||
|
@ -130,6 +132,13 @@ string typeSig(T)() if(canDBus!T) {
|
||||||
return "a" ~ typeSig!(ElementType!T)();
|
return "a" ~ typeSig!(ElementType!T)();
|
||||||
} else static if(isAssociativeArray!T) {
|
} else static if(isAssociativeArray!T) {
|
||||||
return "a{" ~ typeSig!(KeyType!T) ~ typeSig!(ValueType!T) ~ "}";
|
return "a{" ~ typeSig!(KeyType!T) ~ typeSig!(ValueType!T) ~ "}";
|
||||||
|
} else static if(is(T == struct)) {
|
||||||
|
string sig = "(";
|
||||||
|
foreach(i, S; Fields!T) {
|
||||||
|
sig ~= typeSig!S();
|
||||||
|
}
|
||||||
|
sig ~= ")";
|
||||||
|
return sig;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,10 +172,8 @@ string[] typeSigArr(TS...)() if(allCanDBus!TS) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int typeCode(T)() if(canDBus!T) {
|
int typeCode(T)() if(canDBus!T) {
|
||||||
static if (isTuple!T)
|
int code = typeSig!T()[0];
|
||||||
return 'r';
|
return (code != '(') ? code : 'r';
|
||||||
else
|
|
||||||
return typeSig!T()[0];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int typeCode(T)() if(isInstanceOf!(DictionaryEntry, T) && canDBus!(T[])) {
|
int typeCode(T)() if(isInstanceOf!(DictionaryEntry, T) && canDBus!(T[])) {
|
||||||
|
@ -188,9 +195,14 @@ unittest {
|
||||||
// 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());
|
||||||
// structs
|
// 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
|
||||||
|
struct S1 { int a; double b; string s; }
|
||||||
|
typeSig!S1.assertEqual("(ids)");
|
||||||
|
struct S2 { Variant!int c; string d; S1 e; uint f; }
|
||||||
|
typeSig!S2.assertEqual("(vs(ids)u)");
|
||||||
// arrays
|
// arrays
|
||||||
typeSig!(int[]).assertEqual("ai");
|
typeSig!(int[]).assertEqual("ai");
|
||||||
typeSig!(Variant!int[]).assertEqual("av");
|
typeSig!(Variant!int[]).assertEqual("av");
|
||||||
|
|
Loading…
Reference in a new issue