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 }