1 module kwargs.compile_time;
2 
3 
4 /// Designates T to be a required template parameter value of type T
5 struct Required(T) {
6     alias Type = T;
7 }
8 
9 /// Designates the unique template parameter value to be an optional template parameter
10 struct Optional(T...) if(T.length == 1 && !is(T[0])) {
11     alias Type = typeof(T[0]);
12     enum value = T[0];
13 }
14 
15 
16 template kwargify(alias Function, Parameters...)
17     if(__traits(isTemplate, Function) && Parameters.length > 0)
18 {
19     import std.meta: Filter, staticIndexOf;
20 
21     enum isRequired(alias T) = is(T == Required!U, U);
22     alias required = Filter!(isRequired, Parameters);
23 
24     static foreach(i; 0 .. required.length) {
25         static assert(i == staticIndexOf!(required[i], Parameters),
26                       "All `Required` parameters must be at the beginning");
27     }
28 
29     enum isOptional(alias T) = is(T == Optional!U, alias U);
30     alias optional = Filter!(isOptional, Parameters);
31 
32     static foreach(i; 0 .. optional.length) {
33         static assert(i + required.length == staticIndexOf!(optional[i], Parameters),
34                       "All `Optional` parameters must be at the end");
35     }
36 
37     static assert(required.length + optional.length == Parameters.length);
38 
39     auto impl(Args...)() {
40         import std.meta: AliasSeq, staticMap, staticIndexOf, allSatisfy;
41         import std.conv: text;
42 
43         alias Type(alias T) = T.Type;
44         alias ParamTypes = staticMap!(Type, Parameters);
45 
46         enum isParameter(alias T) = staticIndexOf!(typeof(T), ParamTypes) != -1;
47 
48         static assert(allSatisfy!(isParameter, Args),
49                       text("All of `", Args.stringof, "` must be members of `", ParamTypes.stringof, "`"));
50 
51         // return a tuple of values to use as template parameters to `Function`
52         static auto params() {
53             import std.typecons: Tuple;
54 
55             alias TupleType = Tuple!(staticMap!(Type, Parameters));
56 
57             TupleType ret;
58 
59             // required parameters are easy
60             static foreach(i, req; required) {{
61                 alias ofRightType = Filter!(isParamType!req, Args);
62                 static assert(ofRightType.length == 1);
63                 ret[i] = ofRightType[0];
64             }}
65 
66             // optional parameters are trickier
67             static foreach(i, opt; optional) {{
68 
69                 alias ofRightType = Filter!(isParamType!opt, Args);
70 
71                 static if(ofRightType.length == 0) {
72                     // Get the default value from `Parameters` if the user didn't
73                     // supply one of the optional values
74 
75                     alias defaults = Filter!(isParamType!opt, Parameters);
76                     static assert(defaults.length == 1);
77                     ret[required.length + i] = defaults[0].value;
78                 } else {
79                     // value was supplied by the user, use it
80                     static assert(ofRightType.length == 1);
81                     ret[required.length + i] = ofRightType[0];
82                 }
83             }}
84 
85             return ret;
86         }
87 
88         enum paramTuple = params;
89         return Function!(paramTuple.expand);
90     }
91 
92     alias kwargify = impl;
93 }
94 
95 
96 // Workaround for https://issues.dlang.org/show_bug.cgi?id=19650
97 private template isParamType(alias param) {
98 
99     private template Type(alias T) {
100         static if(is(T.Type))
101             alias Type = T.Type;
102         else
103             alias Type = typeof(T);
104     }
105 
106     enum isParamType(alias T) = is(Type!T == param.Type);
107 }