This commit is contained in:
Chris Josten 2021-01-22 00:30:12 +01:00
commit 95a29d0578
5 changed files with 114 additions and 55 deletions

View file

@ -132,7 +132,7 @@ msg.readTuple!(typeof(args))().assertEqual(args);
```
### Basic types
These are the basic types supported by `ddbus`:
`bool`, `byte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `double`, `string`, `ObjectPath`, `InterfaceName`, `BusName`
`bool`, `ubyte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `double`, `string`, `ObjectPath`, `InterfaceName`, `BusName`
ObjectPath, InterfaceName and BusName are typesafe wrappers or aliases around strings which should be used to ensure type-safety. They do not allow implicit casts to each other but can be manually converted to strings either by casting to string.

View file

@ -17,7 +17,7 @@ import std.variant : VariantN;
void buildIter(TS...)(DBusMessageIter* iter, TS args)
if (allCanDBus!TS) {
foreach (index, arg; args) {
alias TS[index] T;
alias T = Unqual!(TS[index]);
static if (is(T == string) || is(T == InterfaceName) || is(T == BusName)) {
immutable(char)* cStr = arg.toStringz();
dbus_message_iter_append_basic(iter, typeCode!T, &cStr);
@ -172,10 +172,10 @@ void buildIter(TS...)(DBusMessageIter* iter, TS args)
}
T readIter(T)(DBusMessageIter* iter)
if (is(T == enum) && !is(T == InterfaceName) && !is(T == BusName) && !is(T == FileDescriptor)) {
if (is(T == enum) && !is(Unqual!T == InterfaceName) && !is(Unqual!T == BusName) && !is(Unqual!T == FileDescriptor)) {
import std.algorithm.searching : canFind;
alias OriginalType!T B;
alias B = Unqual!(OriginalType!T);
B value = readIter!B(iter);
enforce(only(EnumMembers!T).canFind(value), new InvalidValueException(value, T.stringof));
@ -197,9 +197,11 @@ T readIter(T)(DBusMessageIter* iter)
return T(cast(E) value);
}
T readIter(T)(DBusMessageIter* iter)
if (!(is(T == enum) && !is(T == InterfaceName) && !is(T == BusName) && !is(T == FileDescriptor))
&& !isInstanceOf!(BitFlags, T) && canDBus!T) {
U readIter(U)(DBusMessageIter* iter)
if (!(is(U == enum) && !is(Unqual!U == InterfaceName) && !is(Unqual!U == BusName) && !is(U == FileDescriptor))
&& !isInstanceOf!(BitFlags, U) && canDBus!U) {
alias T = Unqual!U;
auto argType = dbus_message_iter_get_arg_type(iter);
T ret;
@ -215,7 +217,7 @@ T readIter(T)(DBusMessageIter* iter)
ret.explicitVariant = true;
}
dbus_message_iter_next(iter);
return ret;
return cast(U) ret;
}
}
@ -227,7 +229,7 @@ T readIter(T)(DBusMessageIter* iter)
const(char)* cStr;
dbus_message_iter_get_basic(iter, &cStr);
string str = cStr.fromStringz().idup; // copy string
static if (is(T == string) || is(T == InterfaceName) || is(T == BusName)) {
static if (is(T == string) || is(T : InterfaceName) || is(T : BusName)) {
ret = cast(T)str;
} else {
ret = ObjectPath(str);
@ -240,20 +242,20 @@ T readIter(T)(DBusMessageIter* iter)
DBusMessageIter sub;
dbus_message_iter_recurse(iter, &sub);
readIterTuple!T(&sub, ret);
} else static if (is(T t : U[], U)) {
assert(dbus_message_iter_get_element_type(iter) == typeCode!U);
} else static if (is(T t : S[], S)) {
assert(dbus_message_iter_get_element_type(iter) == typeCode!S);
DBusMessageIter sub;
dbus_message_iter_recurse(iter, &sub);
while (dbus_message_iter_get_arg_type(&sub) != 0) {
static if (is(U == DictionaryEntry!(K, V), K, V)) {
static if (is(S == DictionaryEntry!(K, V), K, V)) {
DBusMessageIter entry;
dbus_message_iter_recurse(&sub, &entry);
ret ~= U(readIter!K(&entry), readIter!V(&entry));
ret ~= S(readIter!K(&entry), readIter!V(&entry));
dbus_message_iter_next(&sub);
} else {
ret ~= readIter!U(&sub);
ret ~= readIter!S(&sub);
}
}
} else static if (isInstanceOf!(Variant, T)) {
@ -296,13 +298,13 @@ T readIter(T)(DBusMessageIter* iter)
if (ret.type == 's') {
ret.str = readIter!string(iter);
return ret;
return cast(U) ret;
} else if (ret.type == 'o') {
ret.obj = readIter!ObjectPath(iter);
return ret;
return cast(U) ret;
} else if (ret.type == 'b') {
ret.boolean = readIter!bool(iter);
return ret;
return cast(U) ret;
} else if (dbus_type_is_basic(ret.type)) {
dbus_message_iter_get_basic(iter, &ret.int64);
} else if (ret.type == 'a') {
@ -352,7 +354,7 @@ T readIter(T)(DBusMessageIter* iter)
}
dbus_message_iter_next(iter);
return ret;
return cast(U) ret;
}
void readIterTuple(Tup)(DBusMessageIter* iter, ref Tup tuple)
@ -451,7 +453,7 @@ unittest {
z = 4
}
alias V = Algebraic!(byte, short, int, long, string);
alias V = Algebraic!(ubyte, short, int, long, string);
Message msg = Message(busName("org.example.wow"), ObjectPath("/wut"), interfaceName("org.test.iface"), "meth2");
V v1 = "hello from variant";

View file

@ -275,7 +275,7 @@ unittest {
}
struct S {
byte b;
ubyte b;
ulong ul;
F f;
}

View file

@ -46,6 +46,10 @@ struct ObjectPath {
return hashOf(_value);
}
T opCast(T : ObjectPath)() const pure @nogc nothrow @safe {
return this;
}
T opCast(T : string)() const pure @nogc nothrow @safe {
return value;
}
@ -233,7 +237,7 @@ struct DBusAny {
union {
///
byte int8;
ubyte uint8;
///
short int16;
///
@ -284,8 +288,8 @@ struct DBusAny {
+/
this(T)(T value) {
static if (is(T == byte) || is(T == ubyte)) {
this(typeCode!byte, null, false);
int8 = cast(byte) value;
this(typeCode!ubyte, null, false);
uint8 = cast(ubyte) value;
} else static if (is(T == short)) {
this(typeCode!short, null, false);
int16 = cast(short) value;
@ -424,8 +428,8 @@ struct DBusAny {
string toString() const {
string valueStr;
switch (type) {
case typeCode!byte:
valueStr = int8.to!string;
case typeCode!ubyte:
valueStr = uint8.to!string;
break;
case typeCode!short:
valueStr = int16.to!string;
@ -499,8 +503,9 @@ struct DBusAny {
TypeMismatchException if the DBus type of the current value of the
DBusAny object is not the same as the DBus type used to represent T.
+/
T get(T)() @property const
if (staticIndexOf!(T, BasicTypes) >= 0) {
U get(U)() @property const
if (staticIndexOf!(Unqual!U, BasicTypes) >= 0) {
alias T = Unqual!U;
enforce(type == typeCode!T, new TypeMismatchException(
"Cannot get a " ~ T.stringof ~ " from a DBusAny with" ~ " a value of DBus type '" ~ typeSig ~ "'.",
typeCode!T, type));
@ -509,17 +514,17 @@ struct DBusAny {
return fileDescriptor(int32);
} else static if (isIntegral!T) {
enum memberName = (isUnsigned!T ? "uint" : "int") ~ (T.sizeof * 8).to!string;
return __traits(getMember, this, memberName);
return cast(U) __traits(getMember, this, memberName);
} else static if (is(T == double)) {
return float64;
return cast(U) float64;
} else static if (is(T == string)) {
return str;
return cast(U) str;
} else static if (is(T == InterfaceName) || is(T == BusName)) {
return cast(T) str;
return cast(U) str;
} else static if (is(T == ObjectPath)) {
return obj;
return cast(U) obj;
} else static if (is(T == bool)) {
return boolean;
return cast(U) boolean;
} else {
static assert(false);
}
@ -715,6 +720,21 @@ struct DBusAny {
return uint64 == b.uint64;
}
}
/// Returns: true if this variant is an integer.
bool typeIsIntegral() const @property {
return type.dbusIsIntegral;
}
/// Returns: true if this variant is a floating point value.
bool typeIsFloating() const @property {
return type.dbusIsFloating;
}
/// Returns: true if this variant is an integer or a floating point value.
bool typeIsNumeric() const @property {
return type.dbusIsNumeric;
}
}
unittest {
@ -725,27 +745,30 @@ unittest {
return v;
}
void test(T)(T value, DBusAny b) {
void test(bool testGet = false, T)(T value, DBusAny b) {
assertEqual(DBusAny(value), b);
assertEqual(b.to!T, value);
static if (testGet)
assertEqual(b.get!T, value);
b.toString();
}
test(cast(ubyte) 184, set!"int8"(DBusAny('y', null, false), cast(byte) 184));
test(cast(short) 184, set!"int16"(DBusAny('n', null, false), cast(short) 184));
test(cast(ushort) 184, set!"uint16"(DBusAny('q', null, false), cast(ushort) 184));
test(cast(int) 184, set!"int32"(DBusAny('i', null, false), cast(int) 184));
test(cast(uint) 184, set!"uint32"(DBusAny('u', null, false), cast(uint) 184));
test(cast(long) 184, set!"int64"(DBusAny('x', null, false), cast(long) 184));
test(cast(ulong) 184, set!"uint64"(DBusAny('t', null, false), cast(ulong) 184));
test(1.84, set!"float64"(DBusAny('d', null, false), 1.84));
test(true, set!"boolean"(DBusAny('b', null, false), true));
test("abc", set!"str"(DBusAny('s', null, false), "abc"));
test(ObjectPath("/foo/Bar"), set!"obj"(DBusAny('o', null, false), ObjectPath("/foo/Bar")));
test!true(cast(ubyte) 184, set!"uint8"(DBusAny('y', null, false), cast(ubyte) 184));
test(cast(byte) 184, set!"uint8"(DBusAny('y', null, false), cast(byte) 184));
test!true(cast(short) 184, set!"int16"(DBusAny('n', null, false), cast(short) 184));
test!true(cast(ushort) 184, set!"uint16"(DBusAny('q', null, false), cast(ushort) 184));
test!true(cast(int) 184, set!"int32"(DBusAny('i', null, false), cast(int) 184));
test!true(cast(uint) 184, set!"uint32"(DBusAny('u', null, false), cast(uint) 184));
test!true(cast(long) 184, set!"int64"(DBusAny('x', null, false), cast(long) 184));
test!true(cast(ulong) 184, set!"uint64"(DBusAny('t', null, false), cast(ulong) 184));
test!true(1.84, set!"float64"(DBusAny('d', null, false), 1.84));
test!true(true, set!"boolean"(DBusAny('b', null, false), true));
test!true("abc", set!"str"(DBusAny('s', null, false), "abc"));
test!true(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(ubyte) 184), set!"uint8"(DBusAny('y', null, true), cast(ubyte) 184));
test(variant(cast(short) 184), set!"int16"(DBusAny('n', null, true), cast(short) 184));
test(variant(cast(ushort) 184), set!"uint16"(DBusAny('q', null, true), cast(ushort) 184));
test(variant(cast(int) 184), set!"int32"(DBusAny('i', null, true), cast(int) 184));

View file

@ -54,10 +54,11 @@ template allCanDBus(TS...) {
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
alias BasicTypes = AliasSeq!(bool, byte, short, ushort, int, uint, long, ulong,
alias BasicTypes = AliasSeq!(bool, ubyte, short, ushort, int, uint, long, ulong,
double, string, ObjectPath, InterfaceName, BusName, FileDescriptor);
template basicDBus(T) {
template basicDBus(U) {
alias T = Unqual!U;
static if (staticIndexOf!(T, BasicTypes) >= 0) {
enum basicDBus = true;
} else static if (is(T B == enum)) {
@ -70,7 +71,8 @@ template basicDBus(T) {
}
}
template canDBus(T) {
template canDBus(U) {
alias T = Unqual!U;
static if (basicDBus!T || is(T == DBusAny)) {
enum canDBus = true;
} else static if (isInstanceOf!(Variant, T)) {
@ -107,9 +109,10 @@ unittest {
(canDBus!FileDescriptor).assertTrue();
}
string typeSig(T)()
if (canDBus!T) {
static if (is(T == byte)) {
string typeSig(U)()
if (canDBus!U) {
alias T = Unqual!U;
static if (is(T == ubyte)) {
return "y";
} else static if (is(T == bool)) {
return "b";
@ -208,6 +211,33 @@ int typeCode(T)()
return 'e';
}
/**
Params:
type = the type code of a type (first character in a type string)
Returns: true if the given type is an integer.
*/
bool dbusIsIntegral(int type) @property {
return type == 'y' || type == 'n' || type == 'q' || type == 'i' || type == 'u' || type == 'x' || type == 't';
}
/**
Params:
type = the type code of a type (first character in a type string)
Returns: true if the given type is a floating point value.
*/
bool dbusIsFloating(int type) @property {
return type == 'd';
}
/**
Params:
type = the type code of a type (first character in a type string)
Returns: true if the given type is an integer or a floating point value.
*/
bool dbusIsNumeric(int type) @property {
return dbusIsIntegral(type) || dbusIsFloating(type);
}
unittest {
import dunit.toolkit;
@ -219,16 +249,20 @@ unittest {
typeSig!int().assertEqual("i");
typeSig!bool().assertEqual("b");
typeSig!string().assertEqual("s");
typeSig!InterfaceName().assertEqual("s");
typeSig!(immutable(InterfaceName))().assertEqual("s");
typeSig!ObjectPath().assertEqual("o");
typeSig!(immutable(ObjectPath))().assertEqual("o");
typeSig!(Variant!int)().assertEqual("v");
typeSig!FileDescriptor().assertEqual("h");
// enums
enum E : byte {
enum E : ubyte {
a,
b,
c
}
typeSig!E().assertEqual(typeSig!byte());
typeSig!E().assertEqual(typeSig!ubyte());
enum U : string {
One = "One",
Two = "Two"
@ -267,7 +301,7 @@ unittest {
// arrays
typeSig!(int[]).assertEqual("ai");
typeSig!(Variant!int[]).assertEqual("av");
typeSig!(Tuple!(byte)[][]).assertEqual("aa(y)");
typeSig!(Tuple!(ubyte)[][]).assertEqual("aa(y)");
// dictionaries
typeSig!(int[string]).assertEqual("a{si}");
typeSig!(DictionaryEntry!(string, int)[]).assertEqual("a{si}");