1 module kwargs.runtime;
2 
3 
4 /**
5    Wrap `Function` so that it's callable with its parameters in any order.
6    No types should be repeated in its signature.
7  */
8 template kwargify(alias Function) if(!__traits(isTemplate, Function)) {
9 
10     import std.traits: Parameters;
11     import std.conv: text;
12 
13     // Workaround for https://issues.dlang.org/show_bug.cgi?id=19650
14     private size_t numTypes(T, A...)() {
15         size_t ret;
16         static foreach(i; 0 .. A.length)
17             static if(is(A[i] == T))
18                 ++ret;
19         return ret;
20     }
21 
22     static foreach(T; Parameters!Function) {
23         static assert(numTypes!(T, Parameters!Function) == 1,
24                       text("ERROR: `", __traits(identifier, Function), "` does not have unique types: ",
25                            Parameters!Function.stringof));
26     }
27 
28     auto impl(A...)(auto ref A args) {
29         import std.conv: text;
30         import std.typecons: Tuple;
31         import std.meta: staticMap, staticIndexOf, Filter;
32         import std.traits: Unqual, ParameterDefaults, Parameters;
33 
34         alias funcArgTypes = staticMap!(Unqual, Parameters!Function);
35         enum isWrongType(T) = staticIndexOf!(T, funcArgTypes) == -1;
36         alias wrongTypes = Filter!(isWrongType, A);
37 
38         static assert(wrongTypes.length == 0,
39                       text("ERROR: ", wrongTypes.stringof, " are not parameters of ", __traits(identifier, Function)));
40 
41         auto ref nthParam(int index)() {
42             import std.functional: forward;
43 
44             alias Type = funcArgTypes[index];
45 
46             enum hasDefaultValue = !is(ParameterDefaults!Function[index] == void);
47             enum typeIndex = staticIndexOf!(Type, A);
48 
49             static if(typeIndex == -1) {
50                 static assert(hasDefaultValue,
51                               text("Could not find `", Type.stringof, "` in call to ", __traits(identifier, Function)));
52                 return ParameterDefaults!Function[index];
53             } else {
54                 enum numTypes = numTypes!(Type, A);
55                 static assert(numTypes == 1,
56                               text("ERROR: found ", numTypes, " `", Type.stringof, "`s instead of 1"));
57                 return forward!(args[typeIndex]);
58             }
59         }
60 
61         // to avoid a static if on the return type
62         auto call() {
63 
64             static string callMixinStr() {
65                 import std.range: iota;
66                 import std.array: join;
67                 import std.algorithm: map;
68                 import std.conv: text;
69 
70                 auto params = Parameters!Function.length
71                     .iota
72                     .map!(i => text(`nthParam!`, i))
73                     .join(`, `);
74 
75                 return `return Function(` ~ params ~ `);`;
76             }
77 
78             enum str = callMixinStr;
79             mixin(str);
80         }
81 
82         return call;
83     }
84 
85     alias kwargify = impl;
86 }