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

View file

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

View file

@ -46,6 +46,10 @@ struct ObjectPath {
return hashOf(_value); return hashOf(_value);
} }
T opCast(T : ObjectPath)() const pure @nogc nothrow @safe {
return this;
}
T opCast(T : string)() const pure @nogc nothrow @safe { T opCast(T : string)() const pure @nogc nothrow @safe {
return value; return value;
} }
@ -233,7 +237,7 @@ struct DBusAny {
union { union {
/// ///
byte int8; ubyte uint8;
/// ///
short int16; short int16;
/// ///
@ -284,8 +288,8 @@ struct DBusAny {
+/ +/
this(T)(T value) { 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); this(typeCode!ubyte, null, false);
int8 = cast(byte) value; uint8 = cast(ubyte) value;
} else static if (is(T == short)) { } else static if (is(T == short)) {
this(typeCode!short, null, false); this(typeCode!short, null, false);
int16 = cast(short) value; int16 = cast(short) value;
@ -424,8 +428,8 @@ struct DBusAny {
string toString() const { string toString() const {
string valueStr; string valueStr;
switch (type) { switch (type) {
case typeCode!byte: case typeCode!ubyte:
valueStr = int8.to!string; valueStr = uint8.to!string;
break; break;
case typeCode!short: case typeCode!short:
valueStr = int16.to!string; valueStr = int16.to!string;
@ -499,8 +503,9 @@ struct DBusAny {
TypeMismatchException if the DBus type of the current value of the 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. DBusAny object is not the same as the DBus type used to represent T.
+/ +/
T get(T)() @property const U get(U)() @property const
if (staticIndexOf!(T, BasicTypes) >= 0) { if (staticIndexOf!(Unqual!U, BasicTypes) >= 0) {
alias T = Unqual!U;
enforce(type == typeCode!T, new TypeMismatchException( enforce(type == typeCode!T, new TypeMismatchException(
"Cannot get a " ~ T.stringof ~ " from a DBusAny with" ~ " a value of DBus type '" ~ typeSig ~ "'.", "Cannot get a " ~ T.stringof ~ " from a DBusAny with" ~ " a value of DBus type '" ~ typeSig ~ "'.",
typeCode!T, type)); typeCode!T, type));
@ -509,17 +514,17 @@ struct DBusAny {
return fileDescriptor(int32); return fileDescriptor(int32);
} else static if (isIntegral!T) { } else static if (isIntegral!T) {
enum memberName = (isUnsigned!T ? "uint" : "int") ~ (T.sizeof * 8).to!string; 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)) { } else static if (is(T == double)) {
return float64; return cast(U) float64;
} else static if (is(T == string)) { } else static if (is(T == string)) {
return str; return cast(U) str;
} else static if (is(T == InterfaceName) || is(T == BusName)) { } else static if (is(T == InterfaceName) || is(T == BusName)) {
return cast(T) str; return cast(U) str;
} else static if (is(T == ObjectPath)) { } else static if (is(T == ObjectPath)) {
return obj; return cast(U) obj;
} else static if (is(T == bool)) { } else static if (is(T == bool)) {
return boolean; return cast(U) boolean;
} else { } else {
static assert(false); static assert(false);
} }
@ -715,6 +720,21 @@ struct DBusAny {
return uint64 == b.uint64; 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 { unittest {
@ -725,27 +745,30 @@ unittest {
return v; return v;
} }
void test(T)(T value, DBusAny b) { void test(bool testGet = false, T)(T value, DBusAny b) {
assertEqual(DBusAny(value), b); assertEqual(DBusAny(value), b);
assertEqual(b.to!T, value); assertEqual(b.to!T, value);
static if (testGet)
assertEqual(b.get!T, value);
b.toString(); b.toString();
} }
test(cast(ubyte) 184, set!"int8"(DBusAny('y', null, false), cast(byte) 184)); test!true(cast(ubyte) 184, set!"uint8"(DBusAny('y', null, false), cast(ubyte) 184));
test(cast(short) 184, set!"int16"(DBusAny('n', null, false), cast(short) 184)); test(cast(byte) 184, set!"uint8"(DBusAny('y', null, false), cast(byte) 184));
test(cast(ushort) 184, set!"uint16"(DBusAny('q', null, false), cast(ushort) 184)); test!true(cast(short) 184, set!"int16"(DBusAny('n', null, false), cast(short) 184));
test(cast(int) 184, set!"int32"(DBusAny('i', null, false), cast(int) 184)); test!true(cast(ushort) 184, set!"uint16"(DBusAny('q', null, false), cast(ushort) 184));
test(cast(uint) 184, set!"uint32"(DBusAny('u', null, false), cast(uint) 184)); test!true(cast(int) 184, set!"int32"(DBusAny('i', null, false), cast(int) 184));
test(cast(long) 184, set!"int64"(DBusAny('x', null, false), cast(long) 184)); test!true(cast(uint) 184, set!"uint32"(DBusAny('u', null, false), cast(uint) 184));
test(cast(ulong) 184, set!"uint64"(DBusAny('t', null, false), cast(ulong) 184)); test!true(cast(long) 184, set!"int64"(DBusAny('x', null, false), cast(long) 184));
test(1.84, set!"float64"(DBusAny('d', null, false), 1.84)); test!true(cast(ulong) 184, set!"uint64"(DBusAny('t', null, false), cast(ulong) 184));
test(true, set!"boolean"(DBusAny('b', null, false), true)); test!true(1.84, set!"float64"(DBusAny('d', null, false), 1.84));
test("abc", set!"str"(DBusAny('s', null, false), "abc")); test!true(true, set!"boolean"(DBusAny('b', null, false), true));
test(ObjectPath("/foo/Bar"), set!"obj"(DBusAny('o', null, false), ObjectPath("/foo/Bar"))); 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), test(cast(ubyte[])[1, 2, 3], set!"binaryData"(DBusAny('a', ['y'], false),
cast(ubyte[])[1, 2, 3])); 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(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(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)); 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 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!(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); double, string, ObjectPath, InterfaceName, BusName, FileDescriptor);
template basicDBus(T) { template basicDBus(U) {
alias T = Unqual!U;
static if (staticIndexOf!(T, BasicTypes) >= 0) { static if (staticIndexOf!(T, BasicTypes) >= 0) {
enum basicDBus = true; enum basicDBus = true;
} else static if (is(T B == enum)) { } 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)) { static if (basicDBus!T || is(T == DBusAny)) {
enum canDBus = true; enum canDBus = true;
} else static if (isInstanceOf!(Variant, T)) { } else static if (isInstanceOf!(Variant, T)) {
@ -107,9 +109,10 @@ unittest {
(canDBus!FileDescriptor).assertTrue(); (canDBus!FileDescriptor).assertTrue();
} }
string typeSig(T)() string typeSig(U)()
if (canDBus!T) { if (canDBus!U) {
static if (is(T == byte)) { alias T = Unqual!U;
static if (is(T == ubyte)) {
return "y"; return "y";
} else static if (is(T == bool)) { } else static if (is(T == bool)) {
return "b"; return "b";
@ -208,6 +211,33 @@ int typeCode(T)()
return 'e'; 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 { unittest {
import dunit.toolkit; import dunit.toolkit;
@ -219,16 +249,20 @@ unittest {
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!InterfaceName().assertEqual("s");
typeSig!(immutable(InterfaceName))().assertEqual("s");
typeSig!ObjectPath().assertEqual("o");
typeSig!(immutable(ObjectPath))().assertEqual("o");
typeSig!(Variant!int)().assertEqual("v"); typeSig!(Variant!int)().assertEqual("v");
typeSig!FileDescriptor().assertEqual("h"); typeSig!FileDescriptor().assertEqual("h");
// enums // enums
enum E : byte { enum E : ubyte {
a, a,
b, b,
c c
} }
typeSig!E().assertEqual(typeSig!byte()); typeSig!E().assertEqual(typeSig!ubyte());
enum U : string { enum U : string {
One = "One", One = "One",
Two = "Two" Two = "Two"
@ -267,7 +301,7 @@ unittest {
// arrays // arrays
typeSig!(int[]).assertEqual("ai"); typeSig!(int[]).assertEqual("ai");
typeSig!(Variant!int[]).assertEqual("av"); typeSig!(Variant!int[]).assertEqual("av");
typeSig!(Tuple!(byte)[][]).assertEqual("aa(y)"); typeSig!(Tuple!(ubyte)[][]).assertEqual("aa(y)");
// dictionaries // dictionaries
typeSig!(int[string]).assertEqual("a{si}"); typeSig!(int[string]).assertEqual("a{si}");
typeSig!(DictionaryEntry!(string, int)[]).assertEqual("a{si}"); typeSig!(DictionaryEntry!(string, int)[]).assertEqual("a{si}");