Merge branch 'master' into replace-body-with-do
This commit is contained in:
commit
100ce3e60d
20 changed files with 928 additions and 629 deletions
|
@ -3,12 +3,13 @@ module ddbus.thin;
|
|||
|
||||
import core.time : Duration;
|
||||
|
||||
import ddbus.attributes : isAllowedField;
|
||||
import ddbus.c_lib;
|
||||
import ddbus.conv;
|
||||
import ddbus.exception : TypeMismatchException;
|
||||
import ddbus.util;
|
||||
|
||||
import std.meta : staticIndexOf;
|
||||
import std.meta : ApplyRight, Filter, staticIndexOf;
|
||||
import std.string;
|
||||
import std.typecons;
|
||||
import std.exception;
|
||||
|
@ -48,24 +49,28 @@ struct ObjectPath {
|
|||
}
|
||||
|
||||
ObjectPath opBinary(string op : "~")(string rhs) const pure @safe {
|
||||
if (!rhs.startsWith("/"))
|
||||
if (!rhs.startsWith("/")) {
|
||||
return opBinary!"~"(ObjectPath("/" ~ rhs));
|
||||
else
|
||||
} else {
|
||||
return opBinary!"~"(ObjectPath(rhs));
|
||||
}
|
||||
}
|
||||
|
||||
ObjectPath opBinary(string op : "~")(ObjectPath rhs) const pure @safe
|
||||
in {
|
||||
assert(ObjectPath.isValid(_value) && ObjectPath.isValid(rhs._value));
|
||||
} out (v) {
|
||||
}
|
||||
out (v) {
|
||||
assert(ObjectPath.isValid(v._value));
|
||||
} do {
|
||||
}
|
||||
do {
|
||||
ObjectPath ret;
|
||||
|
||||
if (_value == "/")
|
||||
if (_value == "/") {
|
||||
ret._value = rhs._value;
|
||||
else
|
||||
} else {
|
||||
ret._value = _value ~ rhs._value;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -85,20 +90,21 @@ struct ObjectPath {
|
|||
static bool isValid(string objPath) pure @nogc nothrow @safe {
|
||||
import std.ascii : isAlphaNum;
|
||||
|
||||
if (!objPath.length)
|
||||
if (!objPath.length) {
|
||||
return false;
|
||||
if (objPath == "/")
|
||||
}
|
||||
|
||||
if (objPath == "/") {
|
||||
return true;
|
||||
if (objPath[0] != '/' || objPath[$ - 1] == '/')
|
||||
}
|
||||
|
||||
if (objPath[0] != '/' || objPath[$ - 1] == '/') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// .representation to avoid unicode exceptions -> @nogc & nothrow
|
||||
return objPath.representation.splitter('/').drop(1)
|
||||
.all!(a =>
|
||||
a.length &&
|
||||
a.all!(c =>
|
||||
c.isAlphaNum || c == '_'
|
||||
)
|
||||
);
|
||||
return objPath.representation.splitter('/').drop(1).all!(a => a.length
|
||||
&& a.all!(c => c.isAlphaNum || c == '_'));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -137,8 +143,7 @@ struct DBusAny {
|
|||
/// Same functionality as Variant!T but with dynamic types if true.
|
||||
bool explicitVariant;
|
||||
|
||||
union
|
||||
{
|
||||
union {
|
||||
///
|
||||
byte int8;
|
||||
///
|
||||
|
@ -171,117 +176,163 @@ struct DBusAny {
|
|||
ubyte[] binaryData;
|
||||
}
|
||||
|
||||
/// Manually creates a DBusAny object using a type, signature and implicit specifier.
|
||||
/++
|
||||
Manually creates a DBusAny object using a type, signature and explicit
|
||||
variant specifier.
|
||||
|
||||
Direct use of this constructor from user code should be avoided.
|
||||
+/
|
||||
this(int type, string 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.
|
||||
/++
|
||||
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)) {
|
||||
static if (is(T == byte) || is(T == ubyte)) {
|
||||
this(typeCode!byte, null, false);
|
||||
int8 = cast(byte) value;
|
||||
} else static if(is(T == short)) {
|
||||
} else static if (is(T == short)) {
|
||||
this(typeCode!short, null, false);
|
||||
int16 = cast(short) value;
|
||||
} else static if(is(T == ushort)) {
|
||||
} else static if (is(T == ushort)) {
|
||||
this(typeCode!ushort, null, false);
|
||||
uint16 = cast(ushort) value;
|
||||
} else static if(is(T == int)) {
|
||||
} else static if (is(T == int)) {
|
||||
this(typeCode!int, null, false);
|
||||
int32 = cast(int) value;
|
||||
} else static if(is(T == uint)) {
|
||||
} else static if (is(T == uint)) {
|
||||
this(typeCode!uint, null, false);
|
||||
uint32 = cast(uint) value;
|
||||
} else static if(is(T == long)) {
|
||||
} else static if (is(T == long)) {
|
||||
this(typeCode!long, null, false);
|
||||
int64 = cast(long) value;
|
||||
} else static if(is(T == ulong)) {
|
||||
} else static if (is(T == ulong)) {
|
||||
this(typeCode!ulong, null, false);
|
||||
uint64 = cast(ulong) value;
|
||||
} else static if(is(T == double)) {
|
||||
} else static if (is(T == double)) {
|
||||
this(typeCode!double, null, false);
|
||||
float64 = cast(double) value;
|
||||
} else static if(isSomeString!T) {
|
||||
} else static if (isSomeString!T) {
|
||||
this(typeCode!string, null, false);
|
||||
str = value.to!string;
|
||||
} else static if(is(T == bool)) {
|
||||
} else static if (is(T == bool)) {
|
||||
this(typeCode!bool, null, false);
|
||||
boolean = cast(bool) value;
|
||||
} else static if(is(T == ObjectPath)) {
|
||||
} else static if (is(T == ObjectPath)) {
|
||||
this(typeCode!ObjectPath, null, false);
|
||||
obj = value;
|
||||
} else static if(is(T == Variant!R, R)) {
|
||||
static if(is(R == DBusAny)) {
|
||||
} 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') {
|
||||
if(signature == ['y'])
|
||||
if (type == 'a' || type == 'r') {
|
||||
if (signature == ['y']) {
|
||||
binaryData = value.data.binaryData;
|
||||
else
|
||||
} else {
|
||||
array = value.data.array;
|
||||
} else if(type == 's')
|
||||
}
|
||||
} else if (type == 's') {
|
||||
str = value.data.str;
|
||||
else if(type == 'e')
|
||||
} else if (type == 'e') {
|
||||
entry = value.data.entry;
|
||||
else
|
||||
} else {
|
||||
uint64 = value.data.uint64;
|
||||
}
|
||||
} else {
|
||||
this(value.data);
|
||||
explicitVariant = true;
|
||||
}
|
||||
} else static if(is(T : DictionaryEntry!(K, V), K, V)) {
|
||||
} else static if (is(T : DictionaryEntry!(K, V), K, V)) {
|
||||
this('e', null, false);
|
||||
entry = new DictionaryEntry!(DBusAny, DBusAny)();
|
||||
static if(is(K == DBusAny))
|
||||
static if (is(K == DBusAny)) {
|
||||
entry.key = value.key;
|
||||
else
|
||||
} else {
|
||||
entry.key = DBusAny(value.key);
|
||||
static if(is(V == DBusAny))
|
||||
}
|
||||
static if (is(V == DBusAny)) {
|
||||
entry.value = value.value;
|
||||
else
|
||||
} else {
|
||||
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);
|
||||
binaryData = cast(ubyte[]) value;
|
||||
} else static if(isInputRange!T) {
|
||||
} 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");
|
||||
|
||||
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");
|
||||
|
||||
this.signature = .typeSig!(ElementType!T);
|
||||
this.explicitVariant = false;
|
||||
foreach(elem; value)
|
||||
|
||||
foreach (elem; value) {
|
||||
array ~= DBusAny(elem);
|
||||
} else static if(isTuple!T) {
|
||||
}
|
||||
} else static if (isTuple!T) {
|
||||
this.type = 'r';
|
||||
this.signature = ['('];
|
||||
this.explicitVariant = false;
|
||||
foreach(index, R; value.Types) {
|
||||
|
||||
foreach (index, R; value.Types) {
|
||||
auto var = DBusAny(value[index]);
|
||||
tuple ~= var;
|
||||
if(var.explicitVariant)
|
||||
|
||||
if (var.explicitVariant) {
|
||||
this.signature ~= 'v';
|
||||
else {
|
||||
if (var.type != 'r')
|
||||
} else {
|
||||
if (var.type != 'r') {
|
||||
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 ~= ')';
|
||||
} else static if (is(T == struct) && canDBus!T) {
|
||||
this.type = 'r';
|
||||
this.signature = ['('];
|
||||
this.explicitVariant = false;
|
||||
foreach (index, R; Fields!T) {
|
||||
static if (isAllowedField!(value.tupleof[index])) {
|
||||
auto var = DBusAny(value.tupleof[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) {
|
||||
} else static if (isAssociativeArray!T) {
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
string toString() const {
|
||||
string valueStr;
|
||||
switch(type) {
|
||||
switch (type) {
|
||||
case typeCode!byte:
|
||||
valueStr = int8.to!string;
|
||||
break;
|
||||
|
@ -318,10 +369,12 @@ struct DBusAny {
|
|||
case 'a':
|
||||
import std.digest.digest : toHexString;
|
||||
|
||||
if(signature == ['y'])
|
||||
if (signature == ['y']) {
|
||||
valueStr = "binary(" ~ binaryData.toHexString ~ ')';
|
||||
else
|
||||
} else {
|
||||
valueStr = '[' ~ array.map!(a => a.toString).join(", ") ~ ']';
|
||||
}
|
||||
|
||||
break;
|
||||
case 'r':
|
||||
valueStr = '(' ~ tuple.map!(a => a.toString).join(", ") ~ ')';
|
||||
|
@ -333,10 +386,9 @@ struct DBusAny {
|
|||
valueStr = "unknown";
|
||||
break;
|
||||
}
|
||||
return "DBusAny(" ~ cast(char) type
|
||||
~ ", \"" ~ signature.idup
|
||||
~ "\", " ~ (explicitVariant ? "explicit" : "implicit")
|
||||
~ ", " ~ valueStr ~ ")";
|
||||
|
||||
return "DBusAny(" ~ cast(char) type ~ ", \"" ~ signature.idup ~ "\", " ~ (explicitVariant
|
||||
? "explicit" : "implicit") ~ ", " ~ valueStr ~ ")";
|
||||
}
|
||||
|
||||
/++
|
||||
|
@ -354,24 +406,21 @@ struct DBusAny {
|
|||
DBusAny object is not the same as the DBus type used to represent T.
|
||||
+/
|
||||
T get(T)() @property const
|
||||
if(staticIndexOf!(T, BasicTypes) >= 0)
|
||||
{
|
||||
enforce(type == typeCode!T,
|
||||
new TypeMismatchException(
|
||||
"Cannot get a " ~ T.stringof ~ " from a DBusAny with"
|
||||
~ " a value of DBus type '" ~ typeSig ~ "'.", typeCode!T, type));
|
||||
if (staticIndexOf!(T, BasicTypes) >= 0) {
|
||||
enforce(type == typeCode!T, new TypeMismatchException(
|
||||
"Cannot get a " ~ T.stringof ~ " from a DBusAny with" ~ " a value of DBus type '" ~ typeSig ~ "'.",
|
||||
typeCode!T, type));
|
||||
|
||||
static if(isIntegral!T) {
|
||||
enum memberName =
|
||||
(isUnsigned!T ? "uint" : "int") ~ (T.sizeof * 8).to!string;
|
||||
static if (isIntegral!T) {
|
||||
enum memberName = (isUnsigned!T ? "uint" : "int") ~ (T.sizeof * 8).to!string;
|
||||
return __traits(getMember, this, memberName);
|
||||
} else static if(is(T == double)) {
|
||||
} else static if (is(T == double)) {
|
||||
return float64;
|
||||
} else static if(is(T == string)) {
|
||||
} else static if (is(T == string)) {
|
||||
return str;
|
||||
} else static if(is(T == ObjectPath)) {
|
||||
} else static if (is(T == ObjectPath)) {
|
||||
return obj;
|
||||
} else static if(is(T == bool)) {
|
||||
} else static if (is(T == bool)) {
|
||||
return boolean;
|
||||
} else {
|
||||
static assert(false);
|
||||
|
@ -380,38 +429,34 @@ struct DBusAny {
|
|||
|
||||
/// ditto
|
||||
T get(T)() @property const
|
||||
if(is(T == const(DBusAny)[]))
|
||||
{
|
||||
enforce((type == 'a' && signature != "y") || type == 'r',
|
||||
new TypeMismatchException(
|
||||
"Cannot get a " ~ T.stringof ~ " from a DBusAny with"
|
||||
~ " a value of DBus type '" ~ this.typeSig ~ "'.",
|
||||
typeCode!T, type));
|
||||
if (is(T == const(DBusAny)[])) {
|
||||
enforce((type == 'a' && signature != "y") || type == 'r', new TypeMismatchException(
|
||||
"Cannot get a " ~ T.stringof ~ " from a DBusAny with" ~ " a value of DBus type '" ~ this.typeSig ~ "'.",
|
||||
'a', type));
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
/// ditto
|
||||
T get(T)() @property const
|
||||
if (is(T == const(ubyte)[]))
|
||||
{
|
||||
enforce(type == 'a' && signature == "y",
|
||||
new TypeMismatchException(
|
||||
"Cannot get a " ~ T.stringof ~ " from a DBusAny with"
|
||||
~ " a value of DBus type '" ~ this.typeSig ~ "'.",
|
||||
typeCode!T, type));
|
||||
if (is(T == const(ubyte)[])) {
|
||||
enforce(type == 'a' && signature == "y", new TypeMismatchException(
|
||||
"Cannot get a " ~ T.stringof ~ " from a DBusAny with" ~ " a value of DBus type '" ~ this.typeSig ~ "'.",
|
||||
'a', type));
|
||||
|
||||
return binaryData;
|
||||
}
|
||||
|
||||
/// If the value is an array of DictionaryEntries this will return a HashMap
|
||||
DBusAny[DBusAny] toAA() {
|
||||
deprecated("Please use to!(V[K])") DBusAny[DBusAny] toAA() {
|
||||
enforce(type == 'a' && signature && signature[0] == '{');
|
||||
DBusAny[DBusAny] aa;
|
||||
foreach(val; array) {
|
||||
|
||||
foreach (val; array) {
|
||||
enforce(val.type == 'e');
|
||||
aa[val.entry.key] = val.entry.value;
|
||||
}
|
||||
|
||||
return aa;
|
||||
}
|
||||
|
||||
|
@ -421,126 +466,162 @@ struct DBusAny {
|
|||
Returns:
|
||||
The type signature of the value stored in this DBusAny object.
|
||||
+/
|
||||
string typeSig() @property const pure nothrow @safe
|
||||
{
|
||||
if(type == 'a') {
|
||||
string typeSig() @property const pure nothrow @safe {
|
||||
if (type == 'a') {
|
||||
return "a" ~ signature;
|
||||
} else if(type == 'r') {
|
||||
} else if (type == 'r') {
|
||||
return signature;
|
||||
} else if(type == 'e') {
|
||||
return () @trusted {
|
||||
} else if (type == 'e') {
|
||||
return () @trusted{
|
||||
return "{" ~ entry.key.signature ~ entry.value.signature ~ "}";
|
||||
} ();
|
||||
}();
|
||||
} else {
|
||||
return [ cast(char) type ];
|
||||
return [cast(char) type];
|
||||
}
|
||||
}
|
||||
|
||||
/// 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)) {
|
||||
/++
|
||||
Converts a basic type, a tuple or an array to the D type with type checking.
|
||||
|
||||
Tuples can be converted to an array of DBusAny, but not to any other array.
|
||||
+/
|
||||
T to(T)() @property const pure {
|
||||
// Just use `get` if possible
|
||||
static if (canDBus!T && __traits(compiles, get!T)) {
|
||||
if (this.typeSig == .typeSig!T)
|
||||
return get!T;
|
||||
}
|
||||
|
||||
// If we get here, we need some type conversion
|
||||
static if (is(T == Variant!R, R)) {
|
||||
static if (is(R == DBusAny)) {
|
||||
auto v = to!R;
|
||||
v.explicitVariant = false;
|
||||
return Variant!R(v);
|
||||
} else
|
||||
} else {
|
||||
return Variant!R(to!R);
|
||||
} else static if(is(T == DBusAny)) {
|
||||
}
|
||||
} 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 {
|
||||
// In here are all static if blocks that may fall through to the throw
|
||||
// statement at the bottom of this block.
|
||||
|
||||
static if (is(T == DictionaryEntry!(K, V), K, V)) {
|
||||
if (type == 'e') {
|
||||
static if (is(T == typeof(entry))) {
|
||||
return entry;
|
||||
} else {
|
||||
return DictionaryEntry(entry.key.to!K, entry.value.to!V);
|
||||
}
|
||||
}
|
||||
} else static if (isAssociativeArray!T) {
|
||||
if (type == 'a' && (!array.length || array[0].type == 'e')) {
|
||||
alias K = Unqual!(KeyType!T);
|
||||
alias V = Unqual!(ValueType!T);
|
||||
V[K] ret;
|
||||
|
||||
foreach (pair; array) {
|
||||
assert(pair.type == 'e');
|
||||
ret[pair.entry.key.to!K] = pair.entry.value.to!V;
|
||||
}
|
||||
|
||||
return cast(T) ret;
|
||||
}
|
||||
} else static if (isDynamicArray!T && !isSomeString!T) {
|
||||
alias E = Unqual!(ElementType!T);
|
||||
|
||||
if (typeSig == "ay") {
|
||||
auto data = get!(const(ubyte)[]);
|
||||
static if (is(E == ubyte) || is(E == byte)) {
|
||||
return cast(T) data.dup;
|
||||
} else {
|
||||
return cast(T) data.map!(elem => elem.to!E).array;
|
||||
}
|
||||
} else if (type == 'a' || (type == 'r' && is(E == DBusAny))) {
|
||||
return cast(T) get!(const(DBusAny)[]).map!(elem => elem.to!E).array;
|
||||
}
|
||||
} else static if (isTuple!T) {
|
||||
if (type == 'r') {
|
||||
T ret;
|
||||
|
||||
foreach (i, T; ret.Types) {
|
||||
ret[i] = tuple[i].to!T;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
} else static if (is(T == struct) && canDBus!T) {
|
||||
if (type == 'r') {
|
||||
T ret;
|
||||
size_t j;
|
||||
|
||||
foreach (i, F; Fields!T) {
|
||||
static if (isAllowedField!(ret.tupleof[i])) {
|
||||
ret.tupleof[i] = tuple[j++].to!F;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
alias isPreciselyConvertible = ApplyRight!(isImplicitlyConvertible, T);
|
||||
|
||||
template isUnpreciselyConvertible(S) {
|
||||
enum isUnpreciselyConvertible = !isPreciselyConvertible!S
|
||||
&& __traits(compiles, get!S.to!T);
|
||||
}
|
||||
|
||||
// Try to be precise
|
||||
foreach (B; Filter!(isPreciselyConvertible, BasicTypes)) {
|
||||
if (type == typeCode!B)
|
||||
return get!B;
|
||||
}
|
||||
|
||||
// Try to convert
|
||||
foreach (B; Filter!(isUnpreciselyConvertible, BasicTypes)) {
|
||||
if (type == typeCode!B)
|
||||
return get!B.to!T;
|
||||
}
|
||||
}
|
||||
} 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 if(type == 'o')
|
||||
return obj.toString();
|
||||
else
|
||||
throw new Exception("Can't convert type " ~ cast(char) type ~ " to " ~ T.stringof);
|
||||
} else static if(is(T == ObjectPath)) {
|
||||
if(type == 'o')
|
||||
return obj;
|
||||
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;
|
||||
if(signature == ['y']) {
|
||||
static if(isIntegral!(ElementType!T))
|
||||
foreach(elem; binaryData)
|
||||
ret ~= elem.to!(ElementType!T);
|
||||
} else
|
||||
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);
|
||||
|
||||
throw new ConvException("Cannot convert from DBus type '" ~ this.typeSig ~ "' to "
|
||||
~ T.stringof);
|
||||
}
|
||||
}
|
||||
|
||||
bool opEquals(ref in DBusAny b) const {
|
||||
if(b.type != type || b.explicitVariant != explicitVariant)
|
||||
if (b.type != type || b.explicitVariant != explicitVariant) {
|
||||
return false;
|
||||
if((type == 'a' || type == 'r') && b.signature != signature)
|
||||
}
|
||||
|
||||
if ((type == 'a' || type == 'r') && b.signature != signature) {
|
||||
return false;
|
||||
if(type == 'a' && signature == ['y'])
|
||||
}
|
||||
|
||||
if (type == 'a' && signature == ['y']) {
|
||||
return binaryData == b.binaryData;
|
||||
if(type == 'a')
|
||||
}
|
||||
|
||||
if (type == 'a') {
|
||||
return array == b.array;
|
||||
else if(type == 'r')
|
||||
} else if (type == 'r') {
|
||||
return tuple == b.tuple;
|
||||
else if(type == 's')
|
||||
} else if (type == 's') {
|
||||
return str == b.str;
|
||||
else if(type == 'o')
|
||||
} else if (type == 'o') {
|
||||
return obj == b.obj;
|
||||
else if(type == 'e')
|
||||
} else if (type == 'e') {
|
||||
return entry == b.entry || (entry && b.entry && *entry == *b.entry);
|
||||
else
|
||||
} else {
|
||||
return uint64 == b.uint64;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unittest {
|
||||
import dunit.toolkit;
|
||||
|
||||
DBusAny set(string member, T)(DBusAny v, T value) {
|
||||
mixin("v." ~ member ~ " = value;");
|
||||
return v;
|
||||
|
@ -548,15 +629,6 @@ unittest {
|
|||
|
||||
void test(T)(T value, DBusAny b) {
|
||||
assertEqual(DBusAny(value), b);
|
||||
|
||||
static if(is(T == Variant!R, R)) {
|
||||
static if(__traits(compiles, b.get!R))
|
||||
assertEqual(b.get!R, value.data);
|
||||
} else {
|
||||
static if(__traits(compiles, b.get!T))
|
||||
assertEqual(b.get!T, value);
|
||||
}
|
||||
|
||||
assertEqual(b.to!T, value);
|
||||
b.toString();
|
||||
}
|
||||
|
@ -568,8 +640,12 @@ unittest {
|
|||
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(1.84, set!"float64"(DBusAny('d', null, false), 1.84));
|
||||
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("abc", set!"str"(DBusAny('s', null, false), "abc"));
|
||||
test(ObjectPath("/foo/Bar"), set!"obj"(DBusAny('o', null, false), ObjectPath("/foo/Bar")));
|
||||
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(short) 184), set!"int16"(DBusAny('n', null, true), cast(short) 184));
|
||||
|
@ -578,19 +654,29 @@ unittest {
|
|||
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(1.84), set!"float64"(DBusAny('d', null, true), 1.84));
|
||||
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("abc"), set!"str"(DBusAny('s', null, true), "abc"));
|
||||
test(variant(ObjectPath("/foo/Bar")), set!"obj"(DBusAny('o', null, true),
|
||||
ObjectPath("/foo/Bar")));
|
||||
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([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", variant(4), variant([1, 2])), set!"tuple"(DBusAny('r', "(svv)", false), [DBusAny("a"), DBusAny(variant(4)), DBusAny(variant([1, 2]))]));
|
||||
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)", 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([variant("a"): 4], set!"array"(DBusAny('a', "{vi}", false), [DBusAny(DictionaryEntry!(DBusAny, DBusAny)(DBusAny(variant("a")), DBusAny(4)))]));
|
||||
test(["a" : "b"], set!"array"(DBusAny('a', "{ss}", false),
|
||||
[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
|
||||
|
@ -605,17 +691,21 @@ Variant!T variant(T)(T data) {
|
|||
|
||||
enum MessageType {
|
||||
Invalid = 0,
|
||||
Call, Return, Error, Signal
|
||||
Call,
|
||||
Return,
|
||||
Error,
|
||||
Signal
|
||||
}
|
||||
|
||||
struct Message {
|
||||
DBusMessage *msg;
|
||||
DBusMessage* msg;
|
||||
|
||||
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) {
|
||||
msg = m;
|
||||
}
|
||||
|
||||
|
@ -627,7 +717,8 @@ struct Message {
|
|||
dbus_message_unref(msg);
|
||||
}
|
||||
|
||||
void build(TS...)(TS args) if(allCanDBus!TS) {
|
||||
void build(TS...)(TS args)
|
||||
if (allCanDBus!TS) {
|
||||
DBusMessageIter iter;
|
||||
dbus_message_iter_init_append(msg, &iter);
|
||||
buildIter(&iter, args);
|
||||
|
@ -639,14 +730,17 @@ struct Message {
|
|||
read the first argument. This is suitable for single item returns.
|
||||
To read multiple arguments use readTuple.
|
||||
*/
|
||||
T read(T)() if(canDBus!T) {
|
||||
T read(T)()
|
||||
if (canDBus!T) {
|
||||
DBusMessageIter iter;
|
||||
dbus_message_iter_init(msg, &iter);
|
||||
return readIter!T(&iter);
|
||||
}
|
||||
|
||||
alias read to;
|
||||
|
||||
Tup readTuple(Tup)() if(isTuple!Tup && allCanDBus!(Tup.Types)) {
|
||||
Tup readTuple(Tup)()
|
||||
if (isTuple!Tup && allCanDBus!(Tup.Types)) {
|
||||
DBusMessageIter iter;
|
||||
dbus_message_iter_init(msg, &iter);
|
||||
Tup ret;
|
||||
|
@ -659,7 +753,7 @@ struct Message {
|
|||
}
|
||||
|
||||
MessageType type() {
|
||||
return cast(MessageType)dbus_message_get_type(msg);
|
||||
return cast(MessageType) dbus_message_get_type(msg);
|
||||
}
|
||||
|
||||
bool isCall() {
|
||||
|
@ -673,21 +767,25 @@ struct Message {
|
|||
assert(cStr != null);
|
||||
return cStr.fromStringz().idup;
|
||||
}
|
||||
|
||||
string path() {
|
||||
const(char)* cStr = dbus_message_get_path(msg);
|
||||
assert(cStr != null);
|
||||
return cStr.fromStringz().idup;
|
||||
}
|
||||
|
||||
string iface() {
|
||||
const(char)* cStr = dbus_message_get_interface(msg);
|
||||
assert(cStr != null);
|
||||
return cStr.fromStringz().idup;
|
||||
}
|
||||
|
||||
string member() {
|
||||
const(char)* cStr = dbus_message_get_member(msg);
|
||||
assert(cStr != null);
|
||||
return cStr.fromStringz().idup;
|
||||
}
|
||||
|
||||
string sender() {
|
||||
const(char)* cStr = dbus_message_get_sender(msg);
|
||||
assert(cStr != null);
|
||||
|
@ -697,13 +795,14 @@ struct Message {
|
|||
|
||||
unittest {
|
||||
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");
|
||||
}
|
||||
|
||||
struct Connection {
|
||||
DBusConnection *conn;
|
||||
this(DBusConnection *connection) {
|
||||
DBusConnection* conn;
|
||||
this(DBusConnection* connection) {
|
||||
conn = connection;
|
||||
}
|
||||
|
||||
|
@ -720,7 +819,7 @@ struct Connection {
|
|||
}
|
||||
|
||||
void send(Message msg) {
|
||||
dbus_connection_send(conn,msg.msg, null);
|
||||
dbus_connection_send(conn, msg.msg, null);
|
||||
}
|
||||
|
||||
void sendBlocking(Message msg) {
|
||||
|
@ -729,13 +828,13 @@ struct Connection {
|
|||
}
|
||||
|
||||
Message sendWithReplyBlocking(Message msg, int timeout = -1) {
|
||||
DBusMessage *dbusMsg = msg.msg;
|
||||
DBusMessage* dbusMsg = msg.msg;
|
||||
dbus_message_ref(dbusMsg);
|
||||
DBusMessage *reply = wrapErrors((err) {
|
||||
auto ret = dbus_connection_send_with_reply_and_block(conn,dbusMsg,timeout,err);
|
||||
dbus_message_unref(dbusMsg);
|
||||
return ret;
|
||||
});
|
||||
DBusMessage* reply = wrapErrors((err) {
|
||||
auto ret = dbus_connection_send_with_reply_and_block(conn, dbusMsg, timeout, err);
|
||||
dbus_message_unref(dbusMsg);
|
||||
return ret;
|
||||
});
|
||||
return Message(reply);
|
||||
}
|
||||
|
||||
|
@ -758,7 +857,7 @@ unittest {
|
|||
struct S2 {
|
||||
int h, i;
|
||||
@(Yes.DBusMarshal) int j, k;
|
||||
int *p;
|
||||
int* p;
|
||||
}
|
||||
|
||||
@dbusMarshaling(MarshalingFlag.includePrivateFields)
|
||||
|
@ -774,40 +873,34 @@ unittest {
|
|||
|
||||
__gshared int dummy;
|
||||
|
||||
enum testStruct = S3(
|
||||
variant(5), "blah",
|
||||
S1(-7, 63.5, "test"),
|
||||
S2(84, -123, 78, 432, &dummy),
|
||||
16
|
||||
);
|
||||
|
||||
msg.build(testStruct);
|
||||
enum testStruct = S3(variant(5), "blah", S1(-7, 63.5, "test"), S2(84, -123,
|
||||
78, 432, &dummy), 16);
|
||||
|
||||
// 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, null),
|
||||
uint.init
|
||||
);
|
||||
enum expectedResult = S3(variant(5), "blah", S1(int.init, 63.5, "test"),
|
||||
S2(int.init, int.init, 78, 432, null), uint.init);
|
||||
|
||||
// Test struct conversion in building/reading messages
|
||||
msg.build(testStruct);
|
||||
msg.read!S3().assertEqual(expectedResult);
|
||||
|
||||
|
||||
// Test struct conversion in DBusAny
|
||||
DBusAny(testStruct).to!S3.assertEqual(expectedResult);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
unittest {
|
||||
import dunit.toolkit;
|
||||
|
||||
// This test will only pass if DBus is installed.
|
||||
Connection conn = connectToBus();
|
||||
conn.conn.assertTruthy();
|
||||
// We can only count on no system bus on OSX
|
||||
version(OSX) {
|
||||
version (OSX) {
|
||||
connectToBus(DBusBusType.DBUS_BUS_SYSTEM).assertThrow!DBusException();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue