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 }