safer API with ObjectPath, InterfaceName, BusName
the busName and interfacePath types transparently cast down to a string, (but not the other way around) so they are a perfect upgrade to type-safety. The only downside to them is that templates can often get them wrong. std.conv.to will return with a cast(T) prefix, which could slightly break existing code. I think the type-safety advantages are outweighing this corner case though. The current API has been fully upgraded and examples still run and work.
This commit is contained in:
parent
c4de569809
commit
db86451c7f
|
@ -132,7 +132,9 @@ 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`
|
`bool`, `byte`, `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.
|
||||||
|
|
||||||
### Overview of mappings of other types:
|
### Overview of mappings of other types:
|
||||||
|
|
||||||
|
|
|
@ -4,12 +4,12 @@ import ddbus;
|
||||||
|
|
||||||
void testCall(Connection conn) {
|
void testCall(Connection conn) {
|
||||||
for(int i = 0; i < 50; i++) {
|
for(int i = 0; i < 50; i++) {
|
||||||
Message msg = Message("ca.thume.transience","/ca/thume/transience/screensurface",
|
Message msg = Message(busName("ca.thume.transience"), ObjectPath("/ca/thume/transience/screensurface"),
|
||||||
"ca.thume.transience.screensurface","testDot");
|
interfaceName("ca.thume.transience.screensurface"), "testDot");
|
||||||
conn.sendBlocking(msg);
|
conn.sendBlocking(msg);
|
||||||
}
|
}
|
||||||
Message msg2 = Message("ca.thume.transience","/ca/thume/transience/screensurface",
|
Message msg2 = Message(busName("ca.thume.transience"), ObjectPath("/ca/thume/transience/screensurface"),
|
||||||
"ca.thume.transience.screensurface","testPing");
|
interfaceName("ca.thume.transience.screensurface"), "testPing");
|
||||||
Message res = conn.sendWithReplyBlocking(msg2, 3.seconds);
|
Message res = conn.sendWithReplyBlocking(msg2, 3.seconds);
|
||||||
int result = res.read!int();
|
int result = res.read!int();
|
||||||
writeln(result);
|
writeln(result);
|
||||||
|
|
|
@ -3,18 +3,18 @@ import ddbus;
|
||||||
|
|
||||||
void testServe(Connection conn) {
|
void testServe(Connection conn) {
|
||||||
auto router = new MessageRouter();
|
auto router = new MessageRouter();
|
||||||
MessagePattern patt = MessagePattern("/root","ca.thume.test","test");
|
MessagePattern patt = MessagePattern(ObjectPath("/root"), interfaceName("ca.thume.test"), "test");
|
||||||
router.setHandler!(int,int)(patt,(int par) {
|
router.setHandler!(int,int)(patt,(int par) {
|
||||||
writeln("Called with ", par);
|
writeln("Called with ", par);
|
||||||
return par;
|
return par;
|
||||||
});
|
});
|
||||||
patt = MessagePattern("/signaler","ca.thume.test","signal",true);
|
patt = MessagePattern(ObjectPath("/signaler"), interfaceName("ca.thume.test"), "signal",true);
|
||||||
router.setHandler!(void,int)(patt,(int par) {
|
router.setHandler!(void,int)(patt,(int par) {
|
||||||
writeln("Signalled with ", par);
|
writeln("Signalled with ", par);
|
||||||
});
|
});
|
||||||
registerRouter(conn, router);
|
registerRouter(conn, router);
|
||||||
writeln("Getting name...");
|
writeln("Getting name...");
|
||||||
bool gotem = requestName(conn, "ca.thume.ddbus.test");
|
bool gotem = requestName(conn, busName("ca.thume.ddbus.test"));
|
||||||
writeln("Got name: ",gotem);
|
writeln("Got name: ",gotem);
|
||||||
simpleMainLoop(conn);
|
simpleMainLoop(conn);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,9 +5,9 @@ import ddbus.thin;
|
||||||
import ddbus.c_lib;
|
import ddbus.c_lib;
|
||||||
import std.string;
|
import std.string;
|
||||||
|
|
||||||
enum BusService = "org.freedesktop.DBus";
|
enum BusService = busName("org.freedesktop.DBus");
|
||||||
enum BusPath = "/org/freedesktop/DBus";
|
enum BusInterface = interfaceName("org.freedesktop.DBus");
|
||||||
enum BusInterface = "org.freedesktop.DBus";
|
enum BusPath = ObjectPath("/org/freedesktop/DBus");
|
||||||
|
|
||||||
enum NameFlags {
|
enum NameFlags {
|
||||||
AllowReplace = 1,
|
AllowReplace = 1,
|
||||||
|
@ -15,10 +15,16 @@ enum NameFlags {
|
||||||
NoQueue = 4
|
NoQueue = 4
|
||||||
}
|
}
|
||||||
|
|
||||||
|
deprecated("Use the overload taking a BusName instead")
|
||||||
|
bool requestName(Connection conn, string name,
|
||||||
|
NameFlags flags = NameFlags.NoQueue | NameFlags.AllowReplace) {
|
||||||
|
return requestName(conn, busName(name), flags);
|
||||||
|
}
|
||||||
|
|
||||||
/// Requests a DBus well-known name.
|
/// Requests a DBus well-known name.
|
||||||
/// returns if the name is owned after the call.
|
/// returns if the name is owned after the call.
|
||||||
/// Involves blocking call on a DBus method, may throw an exception on failure.
|
/// Involves blocking call on a DBus method, may throw an exception on failure.
|
||||||
bool requestName(Connection conn, string name,
|
bool requestName(Connection conn, BusName name,
|
||||||
NameFlags flags = NameFlags.NoQueue | NameFlags.AllowReplace) {
|
NameFlags flags = NameFlags.NoQueue | NameFlags.AllowReplace) {
|
||||||
auto msg = Message(BusService, BusPath, BusInterface, "RequestName");
|
auto msg = Message(BusService, BusPath, BusInterface, "RequestName");
|
||||||
msg.build(name, cast(uint)(flags));
|
msg.build(name, cast(uint)(flags));
|
||||||
|
@ -44,5 +50,5 @@ unittest {
|
||||||
import dunit.toolkit;
|
import dunit.toolkit;
|
||||||
|
|
||||||
Connection conn = connectToBus();
|
Connection conn = connectToBus();
|
||||||
conn.requestName("ca.thume.ddbus.testing").assertTrue();
|
conn.requestName(busName("ca.thume.ddbus.testing")).assertTrue();
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ 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 TS[index] T;
|
||||||
static if (is(T == string)) {
|
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);
|
||||||
} else static if (is(T == ObjectPath)) {
|
} else static if (is(T == ObjectPath)) {
|
||||||
|
@ -172,7 +172,7 @@ void buildIter(TS...)(DBusMessageIter* iter, TS args)
|
||||||
}
|
}
|
||||||
|
|
||||||
T readIter(T)(DBusMessageIter* iter)
|
T readIter(T)(DBusMessageIter* iter)
|
||||||
if (is(T == enum)) {
|
if (is(T == enum) && !is(T == InterfaceName) && !is(T == BusName)) {
|
||||||
import std.algorithm.searching : canFind;
|
import std.algorithm.searching : canFind;
|
||||||
|
|
||||||
alias OriginalType!T B;
|
alias OriginalType!T B;
|
||||||
|
@ -198,7 +198,7 @@ T readIter(T)(DBusMessageIter* iter)
|
||||||
}
|
}
|
||||||
|
|
||||||
T readIter(T)(DBusMessageIter* iter)
|
T readIter(T)(DBusMessageIter* iter)
|
||||||
if (!is(T == enum) && !isInstanceOf!(BitFlags, T) && canDBus!T) {
|
if (!(is(T == enum) && !is(T == InterfaceName) && !is(T == BusName)) && !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;
|
||||||
|
|
||||||
|
@ -222,12 +222,12 @@ T readIter(T)(DBusMessageIter* iter)
|
||||||
enforce(argType == typeCode!T(), new TypeMismatchException(typeCode!T(), argType));
|
enforce(argType == typeCode!T(), new TypeMismatchException(typeCode!T(), argType));
|
||||||
}
|
}
|
||||||
|
|
||||||
static if (is(T == string) || is(T == ObjectPath)) {
|
static if (is(T == string) || is(T == InterfaceName) || is(T == BusName) || 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) || is(T == InterfaceName) || is(T == BusName)) {
|
||||||
ret = str;
|
ret = cast(T)str;
|
||||||
} else {
|
} else {
|
||||||
ret = ObjectPath(str);
|
ret = ObjectPath(str);
|
||||||
}
|
}
|
||||||
|
@ -378,7 +378,7 @@ unittest {
|
||||||
return Variant!T(data);
|
return Variant!T(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
Message msg = Message("org.example.wow", "/wut", "org.test.iface", "meth");
|
Message msg = Message(busName("org.example.wow"), ObjectPath("/wut"), interfaceName("org.test.iface"), "meth");
|
||||||
bool[] emptyB;
|
bool[] emptyB;
|
||||||
string[string] map;
|
string[string] map;
|
||||||
map["hello"] = "world";
|
map["hello"] = "world";
|
||||||
|
@ -388,22 +388,25 @@ unittest {
|
||||||
anyVar.explicitVariant.assertEqual(false);
|
anyVar.explicitVariant.assertEqual(false);
|
||||||
auto tupleMember = DBusAny(tuple(Variant!int(45), Variant!ushort(5), 32,
|
auto tupleMember = DBusAny(tuple(Variant!int(45), Variant!ushort(5), 32,
|
||||||
[1, 2], tuple(variant(4), 5), map));
|
[1, 2], tuple(variant(4), 5), map));
|
||||||
Variant!DBusAny complexVar = variant(DBusAny(["hello world" : variant(DBusAny(1337)),
|
Variant!DBusAny complexVar = variant(DBusAny([
|
||||||
"array value" : variant(DBusAny([42, 64])), "tuple value"
|
"hello world": variant(DBusAny(1337)),
|
||||||
: variant(tupleMember), "optimized binary data"
|
"array value": variant(DBusAny([42, 64])),
|
||||||
: variant(DBusAny(cast(ubyte[])[1, 2, 3, 4, 5, 6]))]));
|
"tuple value": variant(tupleMember),
|
||||||
|
"optimized binary data": 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"]],
|
auto args = tuple(5, true, "wow", interfaceName("methodName"), var(5.9), [6, 5], tuple(6.2, 4, [["lol"]],
|
||||||
emptyB, var([4, 2])), map, anyVar, complexVar);
|
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("ibssvai(diaasabv)a{ss}tv");
|
||||||
|
|
||||||
msg.read!string().assertThrow!TypeMismatchException();
|
msg.read!string().assertThrow!TypeMismatchException();
|
||||||
msg.readTuple!(Tuple!(int, bool, double)).assertThrow!TypeMismatchException();
|
msg.readTuple!(Tuple!(int, bool, double)).assertThrow!TypeMismatchException();
|
||||||
msg.readTuple!(Tuple!(int, bool, string, double)).assertEqual(tuple(5, true, "wow", 5.9));
|
msg.readTuple!(Tuple!(int, bool, string, InterfaceName, double))
|
||||||
|
.assertEqual(tuple(5, true, "wow", interfaceName("methodName"), 5.9));
|
||||||
|
|
||||||
msg.readTuple!(typeof(args))().assertEqual(args);
|
msg.readTuple!(typeof(args))().assertEqual(args);
|
||||||
DBusMessageIter iter;
|
DBusMessageIter iter;
|
||||||
|
@ -411,6 +414,7 @@ unittest {
|
||||||
readIter!int(&iter).assertEqual(5);
|
readIter!int(&iter).assertEqual(5);
|
||||||
readIter!bool(&iter).assertEqual(true);
|
readIter!bool(&iter).assertEqual(true);
|
||||||
readIter!string(&iter).assertEqual("wow");
|
readIter!string(&iter).assertEqual("wow");
|
||||||
|
readIter!InterfaceName(&iter).assertEqual(interfaceName("methodName"));
|
||||||
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(
|
readIter!(Tuple!(double, int, string[][], bool[], Variant!(int[])))(&iter).assertEqual(
|
||||||
|
@ -418,7 +422,7 @@ unittest {
|
||||||
|
|
||||||
// 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;
|
||||||
readIter!(string[string])(&iter).assertEqual(["hello" : "world"]);
|
readIter!(string[string])(&iter).assertEqual(["hello": "world"]);
|
||||||
auto dict = readIter!(DictionaryEntry!(string, string)[])(&iter2);
|
auto dict = readIter!(DictionaryEntry!(string, string)[])(&iter2);
|
||||||
dict.length.assertEqual(1);
|
dict.length.assertEqual(1);
|
||||||
dict[0].key.assertEqual("hello");
|
dict[0].key.assertEqual("hello");
|
||||||
|
@ -448,7 +452,7 @@ unittest {
|
||||||
|
|
||||||
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(busName("org.example.wow"), ObjectPath("/wut"), interfaceName("org.test.iface"), "meth2");
|
||||||
V v1 = "hello from variant";
|
V v1 = "hello from variant";
|
||||||
V v2 = cast(short) 345;
|
V v2 = cast(short) 345;
|
||||||
msg.build(E.c, 4, 5u, 8u, v1, v2);
|
msg.build(E.c, 4, 5u, 8u, v1, v2);
|
||||||
|
|
|
@ -11,8 +11,8 @@ import std.algorithm;
|
||||||
import std.format;
|
import std.format;
|
||||||
|
|
||||||
struct MessagePattern {
|
struct MessagePattern {
|
||||||
string path;
|
ObjectPath path;
|
||||||
string iface;
|
InterfaceName iface;
|
||||||
string method;
|
string method;
|
||||||
bool signal;
|
bool signal;
|
||||||
|
|
||||||
|
@ -23,7 +23,12 @@ struct MessagePattern {
|
||||||
signal = (msg.type() == MessageType.Signal);
|
signal = (msg.type() == MessageType.Signal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
deprecated("Use the constructor taking a ObjectPath and InterfaceName instead")
|
||||||
this(string path, string iface, string method, bool signal = false) {
|
this(string path, string iface, string method, bool signal = false) {
|
||||||
|
this(ObjectPath(path), interfaceName(iface), method, signal);
|
||||||
|
}
|
||||||
|
|
||||||
|
this(ObjectPath path, InterfaceName iface, string method, bool signal = false) {
|
||||||
this.path = path;
|
this.path = path;
|
||||||
this.iface = iface;
|
this.iface = iface;
|
||||||
this.method = method;
|
this.method = method;
|
||||||
|
@ -48,7 +53,7 @@ struct MessagePattern {
|
||||||
unittest {
|
unittest {
|
||||||
import dunit.toolkit;
|
import dunit.toolkit;
|
||||||
|
|
||||||
auto msg = Message("org.example.test", "/test", "org.example.testing", "testMethod");
|
auto msg = Message(busName("org.example.test"), ObjectPath("/test"), interfaceName("org.example.testing"), "testMethod");
|
||||||
auto patt = new MessagePattern(msg);
|
auto patt = new MessagePattern(msg);
|
||||||
patt.assertEqual(patt);
|
patt.assertEqual(patt);
|
||||||
patt.signal.assertFalse();
|
patt.signal.assertFalse();
|
||||||
|
@ -141,7 +146,12 @@ class MessageRouter {
|
||||||
static string introspectHeader = `<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
static string introspectHeader = `<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
||||||
<node name="%s">`;
|
<node name="%s">`;
|
||||||
|
|
||||||
|
deprecated("Use introspectXML(ObjectPath path) instead")
|
||||||
string introspectXML(string path) {
|
string introspectXML(string path) {
|
||||||
|
return introspectXML(ObjectPath(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
string introspectXML(ObjectPath path) {
|
||||||
// dfmt off
|
// dfmt off
|
||||||
auto methods = callTable
|
auto methods = callTable
|
||||||
.byKey()
|
.byKey()
|
||||||
|
@ -154,7 +164,7 @@ class MessageRouter {
|
||||||
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">`, cast(string)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);
|
||||||
|
@ -174,14 +184,11 @@ class MessageRouter {
|
||||||
app.put("</interface>");
|
app.put("</interface>");
|
||||||
}
|
}
|
||||||
|
|
||||||
string childPath = path;
|
auto childPath = path;
|
||||||
if (!childPath.endsWith("/")) {
|
|
||||||
childPath ~= "/";
|
|
||||||
}
|
|
||||||
|
|
||||||
auto children = callTable.byKey()
|
auto children = callTable.byKey().filter!(a => a.path.startsWith(childPath)
|
||||||
.filter!(a => (a.path.startsWith(childPath)) && !a.signal)().map!(
|
&& a.path != childPath && !a.signal)().map!((s) => s.path.chompPrefix(childPath))
|
||||||
(s) => s.path.chompPrefix(childPath)).map!((s) => s.findSplit("/")[0])
|
.map!((s) => s.value[1 .. $].findSplit("/")[0])
|
||||||
.array().sort().uniq();
|
.array().sort().uniq();
|
||||||
|
|
||||||
foreach (child; children) {
|
foreach (child; children) {
|
||||||
|
@ -192,7 +199,12 @@ class MessageRouter {
|
||||||
return app.data;
|
return app.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
deprecated("Use the method taking an ObjectPath instead")
|
||||||
void handleIntrospect(string path, Message call, Connection conn) {
|
void handleIntrospect(string path, Message call, Connection conn) {
|
||||||
|
handleIntrospect(ObjectPath(path), call, conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleIntrospect(ObjectPath path, Message call, Connection conn) {
|
||||||
auto retMsg = call.createReturn();
|
auto retMsg = call.createReturn();
|
||||||
retMsg.build(introspectXML(path));
|
retMsg.build(introspectXML(path));
|
||||||
conn.sendBlocking(retMsg);
|
conn.sendBlocking(retMsg);
|
||||||
|
@ -233,17 +245,17 @@ unittest {
|
||||||
|
|
||||||
auto router = new MessageRouter();
|
auto router = new MessageRouter();
|
||||||
// set up test messages
|
// set up test messages
|
||||||
MessagePattern patt = MessagePattern("/root", "ca.thume.test", "test");
|
MessagePattern patt = MessagePattern(ObjectPath("/root"), interfaceName("ca.thume.test"), "test");
|
||||||
router.setHandler!(int, int)(patt, (int p) { return 6; });
|
router.setHandler!(int, int)(patt, (int p) { return 6; });
|
||||||
patt = MessagePattern("/root", "ca.thume.tester", "lolwut");
|
patt = MessagePattern(ObjectPath("/root"), interfaceName("ca.thume.tester"), "lolwut");
|
||||||
router.setHandler!(void, int, string)(patt, (int p, string p2) { });
|
router.setHandler!(void, int, string)(patt, (int p, string p2) { });
|
||||||
patt = MessagePattern("/root/wat", "ca.thume.tester", "lolwut");
|
patt = MessagePattern(ObjectPath("/root/wat"), interfaceName("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(ObjectPath("/root/bar"), interfaceName("ca.thume.tester"), "lolwut");
|
||||||
router.setHandler!(Variant!DBusAny, int)(patt, (int p) {
|
router.setHandler!(Variant!DBusAny, int)(patt, (int p) {
|
||||||
return variant(DBusAny(p));
|
return variant(DBusAny(p));
|
||||||
});
|
});
|
||||||
patt = MessagePattern("/root/foo", "ca.thume.tester", "lolwut");
|
patt = MessagePattern(ObjectPath("/root/foo"), interfaceName("ca.thume.tester"), "lolwut");
|
||||||
router.setHandler!(Tuple!(string, string, int), int,
|
router.setHandler!(Tuple!(string, string, int), int,
|
||||||
Variant!DBusAny)(patt, (int p, Variant!DBusAny any) {
|
Variant!DBusAny)(patt, (int p, Variant!DBusAny any) {
|
||||||
Tuple!(string, string, int) ret;
|
Tuple!(string, string, int) ret;
|
||||||
|
@ -252,10 +264,10 @@ unittest {
|
||||||
ret[2] = p;
|
ret[2] = p;
|
||||||
return ret;
|
return ret;
|
||||||
});
|
});
|
||||||
patt = MessagePattern("/troll", "ca.thume.tester", "wow");
|
patt = MessagePattern(ObjectPath("/troll"), interfaceName("ca.thume.tester"), "wow");
|
||||||
router.setHandler!(void)(patt, { return; });
|
router.setHandler!(void)(patt, { return; });
|
||||||
|
|
||||||
patt = MessagePattern("/root/fancy", "ca.thume.tester", "crazyTest");
|
patt = MessagePattern(ObjectPath("/root/fancy"), interfaceName("ca.thume.tester"), "crazyTest");
|
||||||
enum F : ushort {
|
enum F : ushort {
|
||||||
a = 1,
|
a = 1,
|
||||||
b = 8,
|
b = 8,
|
||||||
|
@ -294,13 +306,13 @@ unittest {
|
||||||
// TODO: these tests rely on nondeterministic hash map ordering
|
// TODO: these tests rely on nondeterministic hash map ordering
|
||||||
static string introspectResult = `<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
static string introspectResult = `<!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"><interface name="ca.thume.test"><method name="test"><arg type="i" direction="in"/><arg type="i" direction="out"/></method></interface><interface name="ca.thume.tester"><method name="lolwut"><arg type="i" direction="in"/><arg type="s" direction="in"/></method></interface><node name="bar"/><node name="fancy"/><node name="foo"/><node name="wat"/></node>`;
|
<node name="/root"><interface name="ca.thume.test"><method name="test"><arg type="i" direction="in"/><arg type="i" direction="out"/></method></interface><interface name="ca.thume.tester"><method name="lolwut"><arg type="i" direction="in"/><arg type="s" direction="in"/></method></interface><node name="bar"/><node name="fancy"/><node name="foo"/><node name="wat"/></node>`;
|
||||||
router.introspectXML("/root").assertEqual(introspectResult);
|
router.introspectXML(ObjectPath("/root")).assertEqual(introspectResult);
|
||||||
static string introspectResult2 = `<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
static string introspectResult2 = `<!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/foo"><interface name="ca.thume.tester"><method name="lolwut"><arg type="i" direction="in"/><arg type="v" direction="in"/><arg type="s" direction="out"/><arg type="s" direction="out"/><arg type="i" direction="out"/></method></interface></node>`;
|
<node name="/root/foo"><interface name="ca.thume.tester"><method name="lolwut"><arg type="i" direction="in"/><arg type="v" direction="in"/><arg type="s" direction="out"/><arg type="s" direction="out"/><arg type="i" direction="out"/></method></interface></node>`;
|
||||||
router.introspectXML("/root/foo").assertEqual(introspectResult2);
|
router.introspectXML(ObjectPath("/root/foo")).assertEqual(introspectResult2);
|
||||||
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(ObjectPath("/root/fancy")).assertEqual(introspectResult3);
|
||||||
router.introspectXML("/")
|
router.introspectXML(ObjectPath("/"))
|
||||||
.assertEndsWith(`<node name="/"><node name="root"/><node name="troll"/></node>`);
|
.assertEndsWith(`<node name="/"><node name="root"/><node name="troll"/></node>`);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,17 +8,23 @@ import std.string;
|
||||||
import std.traits;
|
import std.traits;
|
||||||
|
|
||||||
class PathIface {
|
class PathIface {
|
||||||
this(Connection conn, string dest, ObjectPath path, string iface) {
|
this(Connection conn, BusName dest, ObjectPath path, InterfaceName iface) {
|
||||||
this(conn, dest, path.value, iface);
|
|
||||||
}
|
|
||||||
|
|
||||||
this(Connection conn, string dest, string path, string iface) {
|
|
||||||
this.conn = conn;
|
this.conn = conn;
|
||||||
this.dest = dest.toStringz();
|
this.dest = dest.toStringz();
|
||||||
this.path = path.toStringz();
|
this.path = path.value.toStringz();
|
||||||
this.iface = iface.toStringz();
|
this.iface = iface.toStringz();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
deprecated("Use the constructor taking BusName, ObjectPath and InterfaceName instead")
|
||||||
|
this(Connection conn, string dest, ObjectPath path, string iface) {
|
||||||
|
this(conn, busName(dest), path, interfaceName(iface));
|
||||||
|
}
|
||||||
|
|
||||||
|
deprecated("Use the constructor taking BusName, ObjectPath and InterfaceName instead")
|
||||||
|
this(Connection conn, string dest, string path, string iface) {
|
||||||
|
this(conn, busName(dest), ObjectPath(path), interfaceName(iface));
|
||||||
|
}
|
||||||
|
|
||||||
Ret call(Ret, Args...)(string meth, Args args)
|
Ret call(Ret, Args...)(string meth, Args args)
|
||||||
if (allCanDBus!Args && canDBus!Ret) {
|
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()));
|
||||||
|
@ -43,15 +49,20 @@ unittest {
|
||||||
import dunit.toolkit;
|
import dunit.toolkit;
|
||||||
|
|
||||||
Connection conn = connectToBus();
|
Connection conn = connectToBus();
|
||||||
PathIface obj = new PathIface(conn, "org.freedesktop.DBus",
|
PathIface obj = new PathIface(conn, busName("org.freedesktop.DBus"),
|
||||||
"/org/freedesktop/DBus", "org.freedesktop.DBus");
|
ObjectPath("/org/freedesktop/DBus"), interfaceName("org.freedesktop.DBus"));
|
||||||
auto names = obj.GetNameOwner("org.freedesktop.DBus").to!string();
|
auto names = obj.GetNameOwner(interfaceName("org.freedesktop.DBus")).to!BusName();
|
||||||
names.assertEqual("org.freedesktop.DBus");
|
names.assertEqual(busName("org.freedesktop.DBus"));
|
||||||
obj.call!string("GetNameOwner", "org.freedesktop.DBus").assertEqual("org.freedesktop.DBus");
|
obj.call!BusName("GetNameOwner", interfaceName("org.freedesktop.DBus")).assertEqual(busName("org.freedesktop.DBus"));
|
||||||
}
|
}
|
||||||
|
|
||||||
enum SignalMethod;
|
enum SignalMethod;
|
||||||
|
|
||||||
|
deprecated("Use the registerMethods overload taking an ObjectPath and InterfaceName instead")
|
||||||
|
void registerMethods(T : Object)(MessageRouter router, string path, string iface, T obj) {
|
||||||
|
registerMethods(router, ObjectPath(path), interfaceName(iface), obj);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Registers all *possible* methods of an object in a router.
|
Registers all *possible* methods of an object in a router.
|
||||||
It will not register methods that use types that ddbus can't handle.
|
It will not register methods that use types that ddbus can't handle.
|
||||||
|
@ -64,7 +75,7 @@ enum SignalMethod;
|
||||||
and basically do what MessageRouter.setHandler does but avoiding duplication. Then this DBusWrapper!Class
|
and basically do what MessageRouter.setHandler does but avoiding duplication. Then this DBusWrapper!Class
|
||||||
could be instantiated with any object efficiently and placed in the router table with minimal duplication.
|
could be instantiated with any object efficiently and placed in the router table with minimal duplication.
|
||||||
*/
|
*/
|
||||||
void registerMethods(T : Object)(MessageRouter router, string path, string iface, T obj) {
|
void registerMethods(T : Object)(MessageRouter router, ObjectPath path, InterfaceName 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
|
// dfmt off
|
||||||
|
@ -96,8 +107,8 @@ unittest {
|
||||||
|
|
||||||
auto o = new Tester;
|
auto o = new Tester;
|
||||||
auto router = new MessageRouter;
|
auto router = new MessageRouter;
|
||||||
registerMethods(router, "/", "ca.thume.test", o);
|
registerMethods(router, ObjectPath("/"), interfaceName("ca.thume.test"), o);
|
||||||
MessagePattern patt = MessagePattern("/", "ca.thume.test", "wat");
|
MessagePattern patt = MessagePattern(ObjectPath("/"), interfaceName("ca.thume.test"), "wat");
|
||||||
router.callTable.assertHasKey(patt);
|
router.callTable.assertHasKey(patt);
|
||||||
patt.method = "signalRecv";
|
patt.method = "signalRecv";
|
||||||
patt.signal = true;
|
patt.signal = true;
|
||||||
|
|
|
@ -22,6 +22,8 @@ import std.algorithm;
|
||||||
public import ddbus.exception : wrapErrors, DBusException;
|
public import ddbus.exception : wrapErrors, DBusException;
|
||||||
|
|
||||||
struct ObjectPath {
|
struct ObjectPath {
|
||||||
|
enum root = ObjectPath("/");
|
||||||
|
|
||||||
private string _value;
|
private string _value;
|
||||||
|
|
||||||
this(string objPath) pure @safe {
|
this(string objPath) pure @safe {
|
||||||
|
@ -44,10 +46,22 @@ struct ObjectPath {
|
||||||
return hashOf(_value);
|
return hashOf(_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
T opCast(T : string)() const pure @nogc nothrow @safe {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
bool opEquals(ref const typeof(this) b) const pure @nogc nothrow @safe {
|
bool opEquals(ref const typeof(this) b) const pure @nogc nothrow @safe {
|
||||||
return _value == b._value;
|
return _value == b._value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool opEquals(const typeof(this) b) const pure @nogc nothrow @safe {
|
||||||
|
return _value == b._value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool opEquals(string b) const pure @nogc nothrow @safe {
|
||||||
|
return _value == b;
|
||||||
|
}
|
||||||
|
|
||||||
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));
|
||||||
|
@ -83,6 +97,26 @@ struct ObjectPath {
|
||||||
_value = opBinary!"~"(rhs)._value;
|
_value = opBinary!"~"(rhs)._value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool startsWith(ObjectPath withThat) pure @nogc nothrow @safe {
|
||||||
|
if (withThat._value == "/")
|
||||||
|
return true;
|
||||||
|
else if (_value == "/")
|
||||||
|
return false;
|
||||||
|
else
|
||||||
|
return _value.representation.splitter('/').startsWith(withThat._value.representation.splitter('/'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes a prefix from this path and returns the remainder. Keeps leading slashes.
|
||||||
|
/// Returns this unmodified if the prefix doesn't match.
|
||||||
|
ObjectPath chompPrefix(ObjectPath prefix) pure @nogc nothrow @safe {
|
||||||
|
if (prefix._value == "/" || !startsWith(prefix))
|
||||||
|
return this;
|
||||||
|
else if (prefix._value == _value)
|
||||||
|
return ObjectPath.root;
|
||||||
|
else
|
||||||
|
return ObjectPath.assumePath(_value[prefix._value.length .. $]);
|
||||||
|
}
|
||||||
|
|
||||||
/++
|
/++
|
||||||
Returns: `false` for empty strings or strings that don't match the
|
Returns: `false` for empty strings or strings that don't match the
|
||||||
pattern `(/[0-9A-Za-z_]+)+|/`.
|
pattern `(/[0-9A-Za-z_]+)+|/`.
|
||||||
|
@ -106,6 +140,35 @@ struct ObjectPath {
|
||||||
return objPath.representation.splitter('/').drop(1).all!(a => a.length
|
return objPath.representation.splitter('/').drop(1).all!(a => a.length
|
||||||
&& a.all!(c => c.isAlphaNum || c == '_'));
|
&& a.all!(c => c.isAlphaNum || c == '_'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Does an unsafe assignment to an ObjectPath.
|
||||||
|
static ObjectPath assumePath(string path) pure @nogc nothrow @safe {
|
||||||
|
ObjectPath ret;
|
||||||
|
ret._value = path;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Serves as typesafe alias. Instances should be created using busName instead of casting.
|
||||||
|
/// It prevents accidental usage of bus names in other string parameter fields and makes the API clearer.
|
||||||
|
enum BusName : string {
|
||||||
|
none = null
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Casts a bus name argument to a BusName type. May include additional validation in the future.
|
||||||
|
BusName busName(string name) pure @nogc nothrow @safe {
|
||||||
|
return cast(BusName) name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Serves as typesafe alias. Instances should be created using interfaceName instead of casting.
|
||||||
|
/// It prevents accidental usage of interface paths in other string parameter fields and makes the API clearer.
|
||||||
|
enum InterfaceName : string {
|
||||||
|
none = null
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Casts a interface path argument to an InterfaceName type. May include additional validation in the future.
|
||||||
|
InterfaceName interfaceName(string path) pure @nogc nothrow @safe {
|
||||||
|
return cast(InterfaceName) path;
|
||||||
}
|
}
|
||||||
|
|
||||||
unittest {
|
unittest {
|
||||||
|
@ -118,6 +181,20 @@ unittest {
|
||||||
auto obj = ObjectPath(path);
|
auto obj = ObjectPath(path);
|
||||||
obj.value.assertEqual(path);
|
obj.value.assertEqual(path);
|
||||||
obj.toHash().assertEqual(path.hashOf);
|
obj.toHash().assertEqual(path.hashOf);
|
||||||
|
|
||||||
|
ObjectPath("/some/path").startsWith(ObjectPath("/some")).assertTrue();
|
||||||
|
ObjectPath("/some/path").startsWith(ObjectPath("/path")).assertFalse();
|
||||||
|
ObjectPath("/some/path").startsWith(ObjectPath("/")).assertTrue();
|
||||||
|
ObjectPath("/").startsWith(ObjectPath("/")).assertTrue();
|
||||||
|
ObjectPath("/").startsWith(ObjectPath("/some/path")).assertFalse();
|
||||||
|
|
||||||
|
ObjectPath("/some/path").chompPrefix(ObjectPath("/some")).assertEqual(ObjectPath("/path"));
|
||||||
|
ObjectPath("/some/path").chompPrefix(ObjectPath("/bar")).assertEqual(ObjectPath("/some/path"));
|
||||||
|
ObjectPath("/some/path").chompPrefix(ObjectPath("/")).assertEqual(ObjectPath("/some/path"));
|
||||||
|
ObjectPath("/some/path").chompPrefix(ObjectPath("/some/path")).assertEqual(ObjectPath("/"));
|
||||||
|
ObjectPath("/some/path").chompPrefix(ObjectPath("/some/path/extra")).assertEqual(ObjectPath("/some/path"));
|
||||||
|
ObjectPath("/").chompPrefix(ObjectPath("/some")).assertEqual(ObjectPath("/"));
|
||||||
|
ObjectPath("/").chompPrefix(ObjectPath("/")).assertEqual(ObjectPath("/"));
|
||||||
}
|
}
|
||||||
|
|
||||||
unittest {
|
unittest {
|
||||||
|
@ -418,6 +495,8 @@ struct DBusAny {
|
||||||
return float64;
|
return float64;
|
||||||
} else static if (is(T == string)) {
|
} else static if (is(T == string)) {
|
||||||
return str;
|
return str;
|
||||||
|
} else static if (is(T == InterfaceName) || is(T == BusName)) {
|
||||||
|
return cast(T) str;
|
||||||
} else static if (is(T == ObjectPath)) {
|
} else static if (is(T == ObjectPath)) {
|
||||||
return obj;
|
return obj;
|
||||||
} else static if (is(T == bool)) {
|
} else static if (is(T == bool)) {
|
||||||
|
@ -697,14 +776,23 @@ enum MessageType {
|
||||||
Signal
|
Signal
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Represents a message in the dbus system. Use the constructor to
|
||||||
struct Message {
|
struct Message {
|
||||||
DBusMessage* msg;
|
DBusMessage* msg;
|
||||||
|
|
||||||
|
deprecated("Use the constructor taking a BusName, ObjectPath and InterfaceName instead")
|
||||||
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(),
|
msg = dbus_message_new_method_call(dest.toStringz(), path.toStringz(),
|
||||||
iface.toStringz(), method.toStringz());
|
iface.toStringz(), method.toStringz());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Prepares a new method call to an "instance" "object" "interface" "method".
|
||||||
|
this(BusName dest, ObjectPath path, InterfaceName iface, string method) {
|
||||||
|
msg = dbus_message_new_method_call(dest.toStringz(),
|
||||||
|
path.value.toStringz(), iface.toStringz(), method.toStringz());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wraps an existing low level message object.
|
||||||
this(DBusMessage* m) {
|
this(DBusMessage* m) {
|
||||||
msg = m;
|
msg = m;
|
||||||
}
|
}
|
||||||
|
@ -714,7 +802,10 @@ struct Message {
|
||||||
}
|
}
|
||||||
|
|
||||||
~this() {
|
~this() {
|
||||||
dbus_message_unref(msg);
|
if (msg) {
|
||||||
|
dbus_message_unref(msg);
|
||||||
|
msg = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new iterator and puts in the arguments for calling a method.
|
/// Creates a new iterator and puts in the arguments for calling a method.
|
||||||
|
@ -769,16 +860,16 @@ struct Message {
|
||||||
return cStr.fromStringz().idup;
|
return cStr.fromStringz().idup;
|
||||||
}
|
}
|
||||||
|
|
||||||
string path() {
|
ObjectPath 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 ObjectPath(cStr.fromStringz().idup);
|
||||||
}
|
}
|
||||||
|
|
||||||
string iface() {
|
InterfaceName 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 interfaceName(cStr.fromStringz().idup);
|
||||||
}
|
}
|
||||||
|
|
||||||
string member() {
|
string member() {
|
||||||
|
@ -798,7 +889,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(busName("org.example.test"), ObjectPath("/test"), interfaceName("org.example.testing"), "testMethod");
|
||||||
msg.path().assertEqual("/test");
|
msg.path().assertEqual("/test");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -876,7 +967,7 @@ unittest {
|
||||||
@(No.DBusMarshal) uint g;
|
@(No.DBusMarshal) uint g;
|
||||||
}
|
}
|
||||||
|
|
||||||
Message msg = Message("org.example.wow", "/wut", "org.test.iface", "meth3");
|
Message msg = Message(busName("org.example.wow"), ObjectPath("/wut"), interfaceName("org.test.iface"), "meth3");
|
||||||
|
|
||||||
__gshared int dummy;
|
__gshared int dummy;
|
||||||
|
|
||||||
|
|
|
@ -55,7 +55,7 @@ template allCanDBus(TS...) {
|
||||||
+/
|
+/
|
||||||
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, byte, short, ushort, int, uint, long, ulong,
|
||||||
double, string, ObjectPath);
|
double, string, ObjectPath, InterfaceName, BusName);
|
||||||
|
|
||||||
template basicDBus(T) {
|
template basicDBus(T) {
|
||||||
static if (staticIndexOf!(T, BasicTypes) >= 0) {
|
static if (staticIndexOf!(T, BasicTypes) >= 0) {
|
||||||
|
@ -126,7 +126,7 @@ string typeSig(T)()
|
||||||
return "t";
|
return "t";
|
||||||
} else static if (is(T == double)) {
|
} else static if (is(T == double)) {
|
||||||
return "d";
|
return "d";
|
||||||
} else static if (is(T == string)) {
|
} else static if (is(T == string) || is(T == InterfaceName) || is(T == BusName)) {
|
||||||
return "s";
|
return "s";
|
||||||
} else static if (is(T == ObjectPath)) {
|
} else static if (is(T == ObjectPath)) {
|
||||||
return "o";
|
return "o";
|
||||||
|
@ -208,6 +208,10 @@ int typeCode(T)()
|
||||||
unittest {
|
unittest {
|
||||||
import dunit.toolkit;
|
import dunit.toolkit;
|
||||||
|
|
||||||
|
static assert(canDBus!ObjectPath);
|
||||||
|
static assert(canDBus!InterfaceName);
|
||||||
|
static assert(canDBus!BusName);
|
||||||
|
|
||||||
// basics
|
// basics
|
||||||
typeSig!int().assertEqual("i");
|
typeSig!int().assertEqual("i");
|
||||||
typeSig!bool().assertEqual("b");
|
typeSig!bool().assertEqual("b");
|
||||||
|
@ -276,8 +280,8 @@ unittest {
|
||||||
sig.assertEqual("t");
|
sig.assertEqual("t");
|
||||||
static string sig2 = typeSig!(Tuple!(int, string, string));
|
static string sig2 = typeSig!(Tuple!(int, string, string));
|
||||||
sig2.assertEqual("(iss)");
|
sig2.assertEqual("(iss)");
|
||||||
static string sig3 = typeSigAll!(int, string, string);
|
static string sig3 = typeSigAll!(int, string, InterfaceName, BusName);
|
||||||
sig3.assertEqual("iss");
|
sig3.assertEqual("isss");
|
||||||
}
|
}
|
||||||
|
|
||||||
private template AllowedFieldTypes(S)
|
private template AllowedFieldTypes(S)
|
||||||
|
|
Loading…
Reference in a new issue