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