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 (Exception ex) {
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 
76             UniNode varargs = UniNode.emptyArray;
77             UniNode kwargs = UniNode.emptyObject;
78 
79             bool isVarargs = false;
80             int varargsFilled = 0;
81             static foreach (int i; 0 .. PT.length)
82             {
83                 static if (ParameterIdents[i] == "varargs" || ParameterIdents[i] == "kwargs")
84                 {
85                     isVarargs = true;
86                 }
87                 if (params["varargs"].length > i)
88                 {
89                     if (!isVarargs)
90                     {
91                         fillArg!(i, ParameterTypes[i])(ParameterIdents[i], params["varargs"][i]);
92                         filled[ParameterIdents[i]] = true;
93                     }
94                     else
95                         varargs ~= params["varargs"][i];
96                     varargsFilled++;
97                 }
98             }
99             // Filled missed varargs
100             if (varargsFilled < params["varargs"].length)
101                 foreach(i; varargsFilled .. params["varargs"].length)
102                     varargs ~= params["varargs"][i];
103 
104             bool[string] kwargsFilled;
105             static foreach(i, key; ParameterIdents)
106             {
107                 if (key in params["kwargs"])
108                 {
109                     fillArg!(i, ParameterTypes[i])(key, params["kwargs"][key]);
110                     filled[ParameterIdents[i]] = true;
111                     kwargsFilled[key] = true;
112                 }
113             }
114             // Filled missed kwargs
115             foreach (string key, ref UniNode val; params["kwargs"])
116             {
117                 if (key !in kwargsFilled)
118                     kwargs[key] = val;
119             }
120 
121             // Fill varargs/kwargs
122             foreach(i, key; ParameterIdents)
123             {
124                 static if (key == "varargs")
125                     args[i] = varargs;
126                 else static if (key == "kwargs")
127                     args[i] = kwargs;
128             }
129 
130             string[] missedArgs = [];
131             foreach(key, val; filled)
132                 if (!val)
133                     missedArgs ~= key;
134 
135             if (missedArgs.length)
136                 assertTemplate(0, "Missed values for args `%s`".fmt(missedArgs.join(", ")));
137 
138             static if (is (RT == void))
139             {
140                 F(args.expand);
141                 return UniNode(null);
142             }
143             else
144             {
145                 auto ret = F(args.expand);
146                 return ret.serialize!RT;
147             }
148         }
149 
150         return toDelegate(&func);
151     }
152 }