1 /**
2  * Contains data types and functions required by both vibe.rcpchannel.server
3  * and vibe.rpcchannel.client.
4  *
5  * Protocol related definitions are kept in vibe.rpcchannel.protocol.
6  */
7 module vibe.rpcchannel.base;
8 
9 import vibe.core.stream;
10 import vibe.core.sync;
11 
12 import std.traits, std.meta;
13 import std.range : ElementType;
14 
15 import vibe.rpcchannel.protocol;
16 
17 /*
18  * UDA to ignore a method or event when generating RPC stubs.
19  * 
20  * Note: Do not use this directly, use ignoreRPC instead.
21  * Example:
22  * -------------------------
23  * @ignoreRPC void ignoreThis();
24  * -------------------------
25  */
26 struct IgnoreUDA
27 {
28 }
29 
30 /**
31  * UDA to ignore a method or event when generating RPC stubs.
32  * 
33  * Example:
34  * -------------------------
35  * @ignoreRPC void ignoreThis();
36  * -------------------------
37  */
38 enum ignoreRPC = IgnoreUDA.init;
39 
40 /**
41  * Exception thrown when a RPC error occurs.
42  * 
43  * This exception can be thrown by any of the client or server management
44  * methods (such as disconnect), by all client functions calling a remote
45  * RPC function and by all `event.emit()` calls on a server.
46  */
47 class RPCException : Exception
48 {
49     public
50     {
51         /**
52          * Construct a new RPCException.
53          */
54         @safe pure nothrow this(string message, string file = __FILE__,
55             size_t line = __LINE__, Throwable next = null)
56         {
57             super(message, file, line, next);
58         }
59     }
60 }
61 
62 /**
63  * Exception thrown if the connection to the remote side was closed.
64  *
65  * Note: This is a higher layer concept does not imply that the underlying stream is closed.
66  */
67 class DisconnectedException : RPCException
68 {
69     public
70     {
71         /**
72          * Construct a new DisconnectedException.
73          */
74         @safe pure nothrow this(string message = "Connection disconnected",
75             string file = __FILE__, size_t line = __LINE__, Throwable next = null)
76         {
77             super(message, file, line, next);
78         }
79     }
80 }
81 
82 /**
83  * Checks whether some type is a emittable type of the tinyevent library, i.e.
84  * whether `Type.init.emit()` is working.
85  */
86 enum bool isEmittable2(T) = isArray!T && isDelegate!(ElementType!T)
87         && (is(ReturnType!(ElementType!T) == bool) || is(ReturnType!(ElementType!T) == void));
88 
89 /**
90  * Checks whether name is the name of a special function which should be IgnoreUDA
91  * when generating RPC methods. Ignores `startSession`, constructors, destructors,
92  * `toHash` and `toString`.
93  */
94 enum bool isSpecialFunction(string name) = name == "startSession"
95         || name == "__dtor" || (name.length >= 6 && name[0 .. 5] == "__ctor"
96         || name == "toHash" || name == "toString");
97 
98 /**
99  * Returns a tuple of overloads for function member in API
100  * which will be implemented by the RPC server and client.
101  */
102 template APIFunctionOverloads(API, string member)
103 {
104     alias APIFunctionOverloads = MemberFunctionsTuple!(API, member);
105 }
106 
107 /**
108  * Returns a tuple of all overloads for all functions in API which will be
109  * implemented by the RPC server and client.
110  */
111 template APIOverloads(API)
112 {
113     enum derivedMembers = APIFunctions!API;
114     template OverloadMap(string member)
115     {
116         alias OverloadMap = APIFunctionOverloads!(API, member);
117     }
118 
119     alias APIOverloads = staticMap!(OverloadMap, derivedMembers);
120 }
121 
122 /**
123  * Returns a string tuple of all function members in API which will be
124  * implemented by the RPC server and client.
125  */
126 template APIFunctions(API)
127 {
128     enum derivedMembers = __traits(derivedMembers, API);
129 
130     template isValidMember(string member)
131     {
132         // Guards against private members
133         static if (__traits(compiles, __traits(getMember, API, member)))
134         {
135             static if (isSomeFunction!(__traits(getMember, API, member))
136                        && !hasUDA!(__traits(getMember, API, member), IgnoreUDA)
137                        && !isSpecialFunction!member)
138             {
139                 enum isValidMember = true;
140             }
141             else
142             {
143                 enum isValidMember = false;
144             }
145         }
146         else
147         {
148             enum isValidMember = false;
149         }
150     }
151 
152     alias APIFunctions = Filter!(isValidMember, derivedMembers);
153 }
154 
155 /**
156  * Returns a string tuple of all event members in API which will be
157  * implemented by the RPC server and client.
158  */
159 template APIEvents(API)
160 {
161     enum derivedMembers = __traits(derivedMembers, API);
162 
163     template isValidMember(string member)
164     {
165         // Guards against private members
166         static if (__traits(compiles, __traits(getMember, API, member)))
167         {
168             static if (isEmittable2!(typeof(__traits(getMember, API, member)))
169                        && !hasUDA!(__traits(getMember, API, member), IgnoreUDA))
170             {
171                 enum isValidMember = true;
172             }
173             else
174             {
175                 enum isValidMember = false;
176             }
177         }
178         else
179         {
180             enum isValidMember = false;
181         }
182     }
183 
184     alias APIEvents = Filter!(isValidMember, derivedMembers);
185 }