1 /*
2  * Hunt - A high-level D Programming Language Web framework that encourages rapid development and clean, pragmatic design.
3  *
4  * Copyright (C) 2015-2019, HuntLabs
5  *
6  * Website: https://www.huntlabs.net/
7  *
8  * Licensed under the Apache-2.0 License.
9  *
10  */
11 
12 module hunt.framework.view.algo.Wrapper;
13 
14 private
15 {
16     import std.algorithm : min;
17     import std.format : fmt = format;
18     import std.functional : toDelegate;
19     import std.traits;
20     import std.typecons : Tuple;
21     import std.string : join;
22 
23     import hunt.framework.view.Exception : assertTemplate = assertTemplateException;
24     import hunt.framework.view.Uninode;
25 }
26 
27 
28 alias Function = UniNode delegate(UniNode);
29 
30 
31 template wrapper(alias F)
32     if (isSomeFunction!F)
33 {
34     alias ParameterIdents = ParameterIdentifierTuple!F;
35     alias ParameterTypes = Parameters!F;
36     alias ParameterDefs = ParameterDefaults!F;
37     alias RT = ReturnType!F;
38     alias PT = Tuple!ParameterTypes;
39 
40 
41     Function wrapper()
42     {
43         UniNode func (UniNode params)
44         {
45             assertTemplate(params.kind == UniNode.Kind.object, "Non object params");
46             assertTemplate(cast(bool)("varargs" in params), "Missing varargs in params");
47             assertTemplate(cast(bool)("kwargs" in params), "Missing kwargs in params");
48 
49             bool[string] filled;
50             PT args;
51 
52             foreach(i, def; ParameterDefs)
53             {
54                 alias key = ParameterIdents[i];
55                 static if (key == "varargs")
56                     args[i] = UniNode.emptyArray;
57                 else static if (key == "kwargs")
58                     args[i] = UniNode.emptyObject;
59                 else static if (!is(def == void))
60                     args[i] = def;
61                 else
62                     filled[key] = false;
63             }
64 
65             void fillArg(size_t idx, PType)(string key, UniNode val)
66             {
67                 // TODO toBoolType, toStringType
68                 try
69                     args[idx] = val.deserialize!PType;
70                 catch
71                     assertTemplate(0, "Can't deserialize param `%s` from `%s` to `%s` in function `%s`"
72                                             .fmt(key, val.kind, PType.stringof, fullyQualifiedName!F));
73             }
74 
75             UniNode varargs = UniNode.emptyArray;
76             UniNode kwargs = UniNode.emptyObject;
77 
78             bool isVarargs = false;
79             int varargsFilled = 0;
80             static foreach (int i; 0 .. PT.length)
81             {
82                 static if (ParameterIdents[i] == "varargs" || ParameterIdents[i] == "kwargs")
83                 {
84                     isVarargs = true;
85                 }
86                 if (params["varargs"].length > i)
87                 {
88                     if (!isVarargs)
89                     {
90                         fillArg!(i, ParameterTypes[i])(ParameterIdents[i], params["varargs"][i]);
91                         filled[ParameterIdents[i]] = true;
92                     }
93                     else
94                         varargs ~= params["varargs"][i];
95                     varargsFilled++;
96                 }
97             }
98             // Filled missed varargs
99             if (varargsFilled < params["varargs"].length)
100                 foreach(i; varargsFilled .. params["varargs"].length)
101                     varargs ~= params["varargs"][i];
102 
103             bool[string] kwargsFilled;
104             static foreach(i, key; ParameterIdents)
105             {
106                 if (key in params["kwargs"])
107                 {
108                     fillArg!(i, ParameterTypes[i])(key, params["kwargs"][key]);
109                     filled[ParameterIdents[i]] = true;
110                     kwargsFilled[key] = true;
111                 }
112             }
113             // Filled missed kwargs
114             foreach (string key, ref UniNode val; params["kwargs"])
115             {
116                 if (key !in kwargsFilled)
117                     kwargs[key] = val;
118             }
119 
120             // Fill varargs/kwargs
121             foreach(i, key; ParameterIdents)
122             {
123                 static if (key == "varargs")
124                     args[i] = varargs;
125                 else static if (key == "kwargs")
126                     args[i] = kwargs;
127             }
128 
129             string[] missedArgs = [];
130             foreach(key, val; filled)
131                 if (!val)
132                     missedArgs ~= key;
133 
134             if (missedArgs.length)
135                 assertTemplate(0, "Missed values for args `%s`".fmt(missedArgs.join(", ")));
136 
137             static if (is (RT == void))
138             {
139                 F(args.expand);
140                 return UniNode(null);
141             }
142             else
143             {
144                 auto ret = F(args.expand);
145                 return ret.serialize!RT;
146             }
147         }
148 
149         return toDelegate(&func);
150     }
151 }