1 /**
2  * Модуль содержит компоненты для построения клиентов
3  *
4  * Copyright: (c) 2015-2017, Milofon Project.
5  * License: Subject to the terms of the BSD license, as written in the included LICENSE.txt file.
6  * Author: <m.galanin@milofon.org> Maksim Galanin
7  * Date: 2018-02-11
8  */
9 
10 module dango.service.client;
11 
12 public
13 {
14     import dango.service.controller : RpcController, RpcHandler;
15 }
16 
17 private
18 {
19     import std.traits;
20     import std.format : fmt = format;
21     import std.meta;
22     import std.conv : to;
23 
24     import poodinis : DependencyContainer;
25     import proped : Properties;
26 
27     import vibe.internal.meta.codegen;
28 
29     import dango.system.traits;
30     import dango.system.exception;
31     import dango.system.properties;
32     import dango.system.container;
33 
34     import dango.service.protocol;
35     import dango.service.serializer;
36     import dango.service.transport;
37 }
38 
39 
40 
41 class InterfaceClient(I) : I
42 {
43     import std.typecons : Tuple;
44 
45     private
46     {
47         RpcClientProtocol _protocol;
48     }
49 
50 
51     this(RpcClientProtocol protocol)
52     {
53         _protocol = protocol;
54     }
55 
56 
57     // pragma(msg, generateModuleImports!I());
58     mixin(generateModuleImports!I());
59     // pragma(msg, generateMethodHandlers!I);
60     mixin (generateMethodHandlers!I);
61 }
62 
63 
64 /**
65  * Генерация клиента
66  */
67 InterfaceClient!I createRpcClient(I)(RpcClientProtocol protocol)
68 {
69     return new InterfaceClient!(I)(protocol);
70 }
71 
72 
73 /**
74  * Генерация клиента на основе конфигурации
75  */
76 InterfaceClient!I createRpcClient(I)(shared(DependencyContainer) container, Properties config)
77 {
78     Properties trConf = config.getOrEnforce!Properties("transport",
79             "Not defined client transport config");
80     Properties serConf = config.getOrEnforce!Properties("serializer",
81             "Not defined client serializer config");
82     Properties protoConf = config.getOrEnforce!Properties("protocol",
83             "Not defined client protocol config");
84 
85     string serializerName = getNameOrEnforce(serConf,
86             "Not defined client serializer name");
87     string protoName = getNameOrEnforce(protoConf,
88             "Not defined client protocol name");
89     string transportName = getNameOrEnforce(trConf,
90             "Not defined clien transport name");
91 
92     Serializer serializer = container.resolveByName!Serializer(serializerName);
93     serializer.initialize(serConf);
94     configEnforce(serializer !is null,
95             "Serializer '%s' not register".fmt(serializerName));
96 
97     ClientTransport trans = container.resolveByName!ClientTransport(transportName);
98     trans.initialize(trConf);
99     configEnforce(trans !is null,
100             "Transport '%s' not register".fmt(transportName));
101 
102     RpcClientProtocol protocol = container.resolveByName!RpcClientProtocol(protoName);
103     protocol.initialize(serializer, trans);
104     configEnforce(protocol !is null,
105             "Protocol '%s' not register".fmt(protoName));
106 
107     return createRpcClient!I(protocol);
108 }
109 
110 
111 private:
112 
113 
114 string generateMethodHandlers(I)()
115 {
116     string getFullMethod(string method)
117     {
118         enum udas = getUDAs!(I, RpcController);
119         static if (udas.length > 0)
120         {
121             string prefix = udas[0].prefix;
122             if (prefix.length > 0)
123                 return prefix ~ "." ~ method;
124             else
125                 return method;
126         }
127         else
128             return method;
129     }
130 
131 	string ret = q{
132 		import vibe.internal.meta.codegen : CloneFunction;
133     };
134 
135     foreach (string fName; __traits(allMembers, I))
136     {
137         static if(IsPublicMember!(I, fName))
138         {
139             alias member = Alias!(__traits(getMember, I, fName));
140             static if (isCallable!member)
141             {
142                 foreach (attr; __traits(getAttributes, member))
143                 {
144                     static if (is(typeof(attr) == RpcHandler))
145                     {
146                         ret ~= `mixin CloneFunction!(` ~ fName ~ `, q{` ~
147                             generateMethodHandler!(I, fName)(getFullMethod(attr.method))
148                         ~ `});
149                         `;
150                     }
151                 }
152             }
153         }
154     }
155 
156     return ret;
157 }
158 
159 
160 
161 string generateMethodHandler(I, string fName)(string cmd)
162 {
163     alias member = Alias!(__traits(getMember, I, fName));
164     alias ParameterIdents = ParameterIdentifierTuple!member;
165     alias ParameterTypes = ParameterTuple!member;
166 
167     string generateParameterTuple()
168     {
169         string ret = "Tuple!(";
170         foreach(i, key; ParameterIdents)
171         {
172             alias PType = ParameterTypes[i];
173             if (i > 0)
174                 ret ~= ", ";
175             ret ~= "ParameterTypes[" ~ i.to!string ~ "]";
176             ret ~= ", \"" ~ key ~ "\"";
177         }
178         ret ~= ");";
179         return ret;
180     }
181 
182     string generateAssing()
183     {
184         string ret;
185         foreach(i, key; ParameterIdents)
186         {
187             alias PType = ParameterTypes[i];
188             ret ~= "args." ~ key ~ " = " ~ key ~ ";\n";
189         }
190         return ret;
191     }
192 
193     return q{
194         alias ParameterIdents = ParameterIdentifierTuple!%1$s;
195         alias ParameterTypes = ParameterTuple!%1$s;
196         alias ParameterDefs = ParameterDefaults!%1$s;
197         alias RT = ReturnType!%1$s;
198         alias PT = %2$s
199 
200         static if (ParameterIdents.length == 0)
201             auto params = UniNode();
202         else
203         {
204             PT args;
205             foreach (i, def; ParameterDefs)
206             {
207                 static if (!is(def == void))
208                     args[i] = def;
209             }
210             %3$s
211             auto params = marshalObject!PT(args);
212         }
213 
214         auto result = _protocol.request("%4$s", params);
215         return unmarshalObject!(RT)(result);
216     }.fmt(fName, generateParameterTuple(), generateAssing(), cmd);
217 }
218 
219 
220 
221 string generateModuleImports(I)()
222 {
223 	if (!__ctfe)
224 		assert (false);
225 
226 	import vibe.internal.meta.codegen : getRequiredImports;
227 	import std.algorithm : map;
228 	import std.array : join;
229 
230 	auto modules = getRequiredImports!I();
231 	return join(map!(a => "static import " ~ a ~ ";")(modules), "\n");
232 }