1 module structuredrpc; 2 3 import treeserial; 4 5 import std.traits; 6 import std.meta; 7 8 import std.algorithm; 9 import std.range; 10 11 template RPC(Src_) { 12 alias Src = Src_; 13 struct RPC { 14 ubyte id; 15 } 16 } 17 template RPCSend(Src_) { 18 alias Src = Src_; 19 enum RPCSend; 20 } 21 22 class RPCError : Exception { 23 this(string msg, string file = __FILE__, size_t line = __LINE__) { 24 super(msg, file, line); 25 } 26 } 27 28 enum rpcIDs(Src, alias symbol)() { 29 ubyte next = 0; 30 ubyte[] ids = []; 31 foreach (mem; getSymbolsByUDA!(symbol, RPC!Src)) { 32 alias udas = getUDAs!(mem, RPC!Src); 33 static assert(udas.length == 1); 34 static if (is(udas[$-1])) { 35 assert(next+1 != 0); 36 ids ~= next++; 37 } 38 else { 39 ids ~= udas[$-1].id; 40 next = udas[$-1].id+1; 41 } 42 } 43 return ids; 44 } 45 template rpcByID(Src, alias symbol, ubyte id) { 46 import std.algorithm; 47 alias rpcByID = getSymbolsByUDA!(symbol, RPC!Src)[rpcIDs!(Src,symbol).countUntil(id)]; 48 } 49 50 51 template RPCParameters(alias rpc, Src, Connection) { 52 static if (__traits(isTemplate, rpc)) { 53 alias Params = Parameters!(rpc!Src); 54 } 55 else { 56 alias Params = Parameters!rpc; 57 } 58 static if (is(Connection == typeof(null))) { 59 alias RPCParameters = Params; 60 } 61 else static if (is(Params[0] == Connection)) { 62 alias RPCParameters = Params[1..$]; 63 } 64 else static if (is(Params[$-1] == Connection)) { 65 alias RPCParameters = Params[0..$-1]; 66 } 67 else { 68 alias RPCParameters = Params; 69 } 70 } 71 template RPCConnectionsParam(Connection) { 72 static if (is(Connection == typeof(null))) 73 alias RPCConnectionsParam = AliasSeq!(); 74 else 75 alias RPCConnectionsParam = Connection[]; 76 } 77 78 string identifier(string id) { 79 import std.conv; 80 return id.strip('_'); 81 ////return id.find!"a != '_'".array.to!string; 82 } 83 84 mixin template MakeRPCReceive(Src, Connection, alias Serializer) { 85 import std.meta; 86 import std.traits; 87 import std.algorithm; 88 import treeserial; 89 90 template rpcRecv(S:Src) { 91 void rpcRecv(const(ubyte)[] data) { 92 enum ids = rpcIDs!(Src, typeof(this)); 93 ubyte msgID = Serializer.deserialize!ubyte(data); 94 static foreach(id; ids) { 95 if (msgID == id) { 96 alias rpcTemplate = rpcByID!(Src, typeof(this),id); 97 static if (__traits(isTemplate, rpcTemplate)) 98 alias rpc = rpcTemplate!Src; 99 else 100 alias rpc = rpcTemplate; 101 alias Params = Parameters!rpc; 102 Params args; 103 scope (success) 104 rpc(args); 105 foreach (i, Param; Params) { 106 static if (deserializable!Param) { 107 static if (i==Params.length-1) 108 alias Attributes = NoLength; 109 else 110 alias Attributes = AliasSeq!(); 111 args[i] = Serializer.Subserializer!(Attributes).deserialize!Param(data); 112 } 113 else throw new RPCError("Parameter \""~Param.stringof~"\" of RPC \""~__traits(identifier, rpc)~"\" cannot be deserialized. If it is a ConnectionType then you called the wrong rpcRecv (call rpcRecv with a connection)."); 114 } 115 } 116 } 117 } 118 static if (!is(Connection == typeof(null))) 119 void rpcRecv(Connection connection, const(ubyte)[] data){//// if (!is(ConnectionType!(RPCSrc, src) == typeof(null))) { 120 enum ids = rpcIDs!(Src, typeof(this)); 121 ubyte msgID = Serializer.deserialize!ubyte(data); 122 static foreach(id; ids) { 123 if (msgID == id) { 124 alias rpcTemplate = rpcByID!(Src, typeof(this),id); 125 static if (__traits(isTemplate, rpcTemplate)) 126 alias rpc = rpcTemplate!Src; 127 else 128 alias rpc = rpcTemplate; 129 alias Params = RPCParameters!(rpc, Src, Connection); 130 Params args; 131 scope (success) { 132 static if (is(Parameters!rpc[0] == Connection)) 133 rpc(connection, args); 134 else static if (is(Parameters!rpc[$-1] == Connection)) 135 rpc(args, connection); 136 else 137 rpc(args); 138 } 139 foreach (i, Param; Params) { 140 static if (deserializable!Param) { 141 static if (i==Params.length-1) 142 alias Attributes = NoLength; 143 else 144 alias Attributes = AliasSeq!(); 145 args[i] = Serializer.Subserializer!(Attributes).deserialize!Param(data); 146 } 147 else throw new RPCError("Parameter `"~Param.stringof~"` of RPC `"~__traits(identifier, rpcTemplate)~"` cannot be deserialized.");//// If you think this should be the connection type, then for `"~RPCSrc.stringof~"."~__traits(identifier, src)~"` it should be of type `"~ConnectionType!src.stringof~"` (Correct parameter or `SrcType` or src)."); 148 } 149 } 150 } 151 } 152 } 153 } 154 mixin template MakeRPCReceive(Src) { 155 import treeserial; 156 mixin MakeRPCReceive!(Src, typeof(null), Serializer!()); 157 } 158 mixin template MakeRPCReceive(Src, alias Serializer) { 159 mixin MakeRPCReceive!(Src, typeof(null), Serializer); 160 } 161 mixin template MakeRPCReceive(Src, Connection) { 162 import treeserial; 163 mixin MakeRPCReceive!(Src, Connection, Serializer!()); 164 } 165 166 167 mixin template MakeRPCSendToImpl(SendTo, Src, ToConnection, Connection, alias Serializer) { 168 import std.meta; 169 import std.traits; 170 import std.algorithm; 171 import treeserial; 172 173 static foreach (i, rpc; getSymbolsByUDA!(SendTo, RPC!Src)) { 174 mixin(q{ 175 template }~__traits(identifier, rpc).identifier~q{_send(S:Src) }~"{"~q{ 176 auto }~__traits(identifier, rpc).identifier~q{_send(RPCConnectionsParam!Connection connections, const RPCParameters!(rpc, Src, ToConnection) args) { 177 const(ubyte)[] data = Serializer.serialize!ubyte(rpcIDs!(Src, SendTo)[i]); 178 alias rpcSend = getSymbolsByUDA!(typeof(this), RPCSend!Src)[0]; 179 foreach (i, arg; args) { 180 static if (serializable!arg) { 181 static if (i==args.length-1) 182 alias Attributes = NoLength; 183 else 184 alias Attributes = AliasSeq!(); 185 data ~= Serializer.Subserializer!Attributes.serialize(arg); 186 } 187 else throw new RPCError("Parameter \""~typeof(arg).stringof~"\" of RPC \""~__traits(identifier, rpc)~"\" cannot be serialized."); 188 } 189 return rpcSend(connections, data); 190 } 191 192 // So last last argument can be different for each client. 193 static if (RPCParameters!(rpc, Src, ToConnection).length >= 1) 194 auto }~__traits(identifier, rpc).identifier~q{_send(RPCConnectionsParam!Connection connections, const RPCParameters!(rpc, Src, ToConnection)[0..$-1] args, const RPCParameters!(rpc, Src, ToConnection)[$-1][] lastArgs) { 195 assert(connections.length == lastArgs.length); 196 ubyte[] data = Serializer.serialize!ubyte(rpcIDs!(Src, SendTo)[i]); 197 alias rpcSend = getSymbolsByUDA!(typeof(this), RPCSend!Src)[0]; 198 foreach (i, arg; args) { 199 static if (serializable!arg) { 200 data ~= Serializer.Subserializer!().serialize(arg); 201 } 202 else throw new RPCError("Parameter \""~typeof(arg).stringof~"\" of RPC \""~__traits(identifier, rpc)~"\" cannot be serialized."); 203 } 204 foreach (i, connection; connections) { 205 data.assumeSafeAppend; 206 rpcSend([connection], data~Serializer.Subserializer!NoLength.serialize(lastArgs[i])); 207 } 208 } 209 210 static if (!is(Connection == typeof(null))) 211 auto }~__traits(identifier, rpc).identifier~q{_send(Connection connection, const RPCParameters!(rpc, Src, Connection) args) }~"{ 212 return "~__traits(identifier, rpc).identifier~"_send([connection], args); 213 } 214 }"~q{ 215 }); 216 } 217 } 218 mixin template MakeRPCSendTo(SendTo, Src, ConnectionTo, alias Serializer) { 219 import std.traits; 220 static if (Parameters!(getSymbolsByUDA!(typeof(this), RPCSend!Src)[0]).length==1) 221 mixin MakeRPCSendToImpl!(SendTo, Src, ConnectionTo, typeof(null), Serializer); 222 else 223 mixin MakeRPCSendToImpl!(SendTo, Src, ConnectionTo, ForeachType!(Parameters!(getSymbolsByUDA!(typeof(this), RPCSend!Src)[0])[0]), Serializer); 224 } 225 mixin template MakeRPCSendTo(SendTo, Src) { 226 import treeserial; 227 mixin MakeRPCSendTo!(SendTo, Src, typeof(null), Serializer!()); 228 } 229 mixin template MakeRPCSendTo(SendTo, Src, alias Serializer) { 230 mixin MakeRPCSendTo!(SendTo, Src, typeof(null), Serializer); 231 } 232 mixin template MakeRPCSendTo(SendTo, Src, Connection) { 233 import treeserial; 234 mixin MakeRPCSendTo!(SendTo, Src, Connection, Serializer!()); 235 } 236 237 mixin template MakeRPCSend(Src, Connection, alias Serializer) { 238 mixin MakeRPCSendTo!(typeof(this), Src, Connection, Serializer); 239 } 240 mixin template MakeRPCSend(Src) { 241 import treeserial; 242 mixin MakeRPCSendTo!(typeof(this), Src, typeof(null), Serializer!()); 243 } 244 mixin template MakeRPCSend(Src, alias Serializer) { 245 mixin MakeRPCSendTo!(typeof(this), Src, typeof(null), Serializer); 246 } 247 mixin template MakeRPCSend(Src, Connection) { 248 import treeserial; 249 mixin MakeRPCSendTo!(typeof(this), Src, Connection, Serializer!()); 250 } 251 252