1 module ddbus.simple;
2 
3 import ddbus.thin;
4 import ddbus.util;
5 import ddbus.c_lib;
6 import ddbus.router;
7 import std.string;
8 import std.traits;
9 
10 class PathIface {
11   this(Connection conn, BusName dest, ObjectPath path, InterfaceName iface) {
12     this.conn = conn;
13     this.dest = dest.toStringz();
14     this.path = path.value.toStringz();
15     this.iface = iface.toStringz();
16   }
17 
18   deprecated("Use the constructor taking BusName, ObjectPath and InterfaceName instead")
19   this(Connection conn, string dest, ObjectPath path, string iface) {
20     this(conn, busName(dest), path, interfaceName(iface));
21   }
22 
23   deprecated("Use the constructor taking BusName, ObjectPath and InterfaceName instead")
24   this(Connection conn, string dest, string path, string iface) {
25     this(conn, busName(dest), ObjectPath(path), interfaceName(iface));
26   }
27 
28   Ret call(Ret, Args...)(string meth, Args args)
29       if (allCanDBus!Args && canDBus!Ret) {
30     Message msg = Message(dbus_message_new_method_call(dest, path, iface, meth.toStringz()));
31     msg.build(args);
32     Message ret = conn.sendWithReplyBlocking(msg);
33     return ret.read!Ret();
34   }
35 
36   void call(Ret, Args...)(string meth, Args args)
37       if (allCanDBus!Args && is(Ret == void)) {
38     Message msg = Message(dbus_message_new_method_call(dest, path, iface, meth.toStringz()));
39     msg.build(args);
40     conn.sendBlocking(msg);
41   }
42 
43   Message opDispatch(string meth, Args...)(Args args) {
44     Message msg = Message(dbus_message_new_method_call(dest, path, iface, meth.toStringz()));
45     msg.build(args);
46     return conn.sendWithReplyBlocking(msg);
47   }
48 
49   Connection conn;
50   const(char)* dest;
51   const(char)* path;
52   const(char)* iface;
53 }
54 
55 unittest {
56   import dunit.toolkit;
57 
58   Connection conn = connectToBus();
59   PathIface obj = new PathIface(conn, busName("org.freedesktop.DBus"),
60       ObjectPath("/org/freedesktop/DBus"), interfaceName("org.freedesktop.DBus"));
61   auto names = obj.GetNameOwner(interfaceName("org.freedesktop.DBus")).to!BusName();
62   names.assertEqual(busName("org.freedesktop.DBus"));
63   obj.call!BusName("GetNameOwner", interfaceName("org.freedesktop.DBus")).assertEqual(busName("org.freedesktop.DBus"));
64 }
65 
66 enum SignalMethod;
67 
68 deprecated("Use the registerMethods overload taking an ObjectPath and InterfaceName instead")
69 void registerMethods(T : Object)(MessageRouter router, string path, string iface, T obj) {
70   registerMethods(router, ObjectPath(path), interfaceName(iface), obj);
71 }
72 
73 /**
74    Registers all *possible* methods of an object in a router.
75    It will not register methods that use types that ddbus can't handle.
76 
77    The implementation is rather hacky and uses the compiles trait to check for things
78    working so if some methods randomly don't seem to be added, you should probably use
79    setHandler on the router directly. It is also not efficient and creates a closure for every method.
80 
81    TODO: replace this with something that generates a wrapper class who's methods take and return messages
82    and basically do what MessageRouter.setHandler does but avoiding duplication. Then this DBusWrapper!Class
83    could be instantiated with any object efficiently and placed in the router table with minimal duplication.
84  */
85 void registerMethods(T : Object)(MessageRouter router, ObjectPath path, InterfaceName iface, T obj) {
86   MessagePattern patt = MessagePattern(path, iface, "", false);
87   foreach (member; __traits(allMembers, T)) {
88     // dfmt off
89     static if (__traits(compiles, __traits(getOverloads, obj, member))
90         && __traits(getOverloads, obj, member).length > 0
91         && __traits(compiles, router.setHandler(patt, &__traits(getOverloads, obj, member)[0]))) {
92       patt.method = member;
93       patt.signal = hasUDA!(__traits(getOverloads, obj, member)[0], SignalMethod);
94       router.setHandler(patt, &__traits(getOverloads, obj, member)[0]);
95     }
96     // dfmt on
97   }
98 }
99 
100 unittest {
101   import dunit.toolkit;
102 
103   class Tester {
104     int lol(int x, string s, string[string] map, Variant!DBusAny any) {
105       return 5;
106     }
107 
108     void wat() {
109     }
110 
111     @SignalMethod void signalRecv() {
112     }
113   }
114 
115   auto o = new Tester;
116   auto router = new MessageRouter;
117   registerMethods(router, ObjectPath("/"), interfaceName("ca.thume.test"), o);
118   MessagePattern patt = MessagePattern(ObjectPath("/"), interfaceName("ca.thume.test"), "wat");
119   router.callTable.assertHasKey(patt);
120   patt.method = "signalRecv";
121   patt.signal = true;
122   router.callTable.assertHasKey(patt);
123   patt.method = "lol";
124   patt.signal = false;
125   router.callTable.assertHasKey(patt);
126   auto res = router.callTable[patt];
127   res.argSig.assertEqual(["i", "s", "a{ss}", "v"]);
128   res.retSig.assertEqual(["i"]);
129 }