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 }