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-01-28 8 */ 9 10 module dango.service.dispatcher; 11 12 private 13 { 14 import std.functional : toDelegate; 15 import std.typecons : Tuple; 16 import std.traits; 17 import std.format : fmt = format; 18 19 import vibe.core.log; 20 21 import dango.service.serializer : UniNode, 22 marshalObject, unmarshalObject; 23 import dango.service.protocol; 24 } 25 26 27 alias Handler = UniNode delegate(UniNode params); 28 29 30 class Dispatcher 31 { 32 private 33 { 34 Handler[string] _handlers; 35 } 36 37 38 bool existst(string cmd) 39 { 40 return (cmd in _handlers) !is null; 41 } 42 43 44 UniNode handler(string cmd, UniNode params) 45 { 46 if (auto h = cmd in _handlers) 47 return (*h)(params); 48 else 49 throw new RpcException(createEmptyErrorByCode!UniNode( 50 ErrorCode.METHOD_NOT_FOUND)); 51 } 52 53 54 void registerHandler(string cmd, Handler hdl) 55 { 56 _handlers[cmd] = hdl; 57 logInfo("Register method (%s)", cmd); 58 } 59 60 61 template generateHandler(alias F) 62 { 63 alias ParameterIdents = ParameterIdentifierTuple!F; 64 alias ParameterTypes = ParameterTypeTuple!F; 65 alias ParameterDefs = ParameterDefaults!F; 66 alias Type = typeof(toDelegate(&F)); 67 alias RT = ReturnType!F; 68 alias PT = Tuple!ParameterTypes; 69 70 Handler generateHandler(Type hdl) 71 { 72 bool[string] requires; // обязательные поля 73 74 UniNode fun(UniNode params) 75 { 76 if (!(params.type == UniNode.Type.object 77 || params.type == UniNode.Type.array)) 78 throw new RpcException(createEmptyErrorByCode!UniNode( 79 ErrorCode.INVALID_PARAMS)); 80 81 string[][string] paramErrors; 82 83 // инициализируем обязательные поля 84 PT args; 85 foreach (i, def; ParameterDefs) 86 { 87 string key = ParameterIdents[i]; 88 static if (is(def == void)) 89 requires[key] = false; 90 else 91 args[i] = def; 92 } 93 94 95 void fillArg(size_t idx, PType)(string key, UniNode value) 96 { 97 try 98 args[idx] = unmarshalObject!(PType)(value); 99 catch (Exception e) 100 paramErrors[key] ~= "Got type %s, expected %s".fmt( 101 value.type, typeid(PType)); 102 } 103 104 foreach(i, key; ParameterIdents) 105 { 106 alias PType = ParameterTypes[i]; 107 if (params.type == UniNode.Type.object) 108 { 109 auto pObj = params.via.map; 110 if (auto v = key in pObj) 111 { 112 fillArg!(i, PType)(key, *v); 113 requires[key] = true; 114 } 115 else 116 { 117 if (ParameterIdents.length == 1 && isAggregateType!PType) 118 { 119 fillArg!(i, PType)(key, params); 120 requires[key] = true; 121 } 122 } 123 } 124 else if (params.type == UniNode.Type.array) 125 { 126 UniNode[] aParams = params.get!(UniNode[]); 127 if (isArray!PType && ParameterIdents.length == 1) 128 { 129 fillArg!(i, PType)(key, params); 130 requires[key] = true; 131 } 132 else if (i < aParams.length) 133 { 134 UniNode v = aParams[i]; 135 fillArg!(i, PType)(key, v); 136 requires[key] = true; 137 } 138 } 139 } 140 141 foreach (k, v; requires) 142 { 143 if (v == false) 144 paramErrors[k] ~= "is required"; 145 } 146 147 if (paramErrors.length > 0) 148 { 149 UniNode[string] errObj; 150 foreach (k, errs; paramErrors) 151 { 152 logInfo("%s -> %s", k, errs); 153 UniNode[] errArr; 154 foreach (v; errs) 155 errArr ~= UniNode(v); 156 errObj[k] = UniNode(errArr); 157 } 158 159 throw new RpcException(createErrorByCode!UniNode( 160 ErrorCode.INVALID_PARAMS, 161 UniNode(errObj))); 162 } 163 164 RT ret = hdl(args.expand); 165 return marshalObject!RT(ret); 166 } 167 168 return &fun; 169 } 170 } 171 }