1 // This file is written in D programming language
2 /**
3 *   Module holds compile-time associative map with heterogeneous keys and values.
4 *
5 *   Copyright: © 2013-2014 Anton Gushcha
6 *   License: Subject to the terms of the MIT license, as written in the included LICENSE file.
7 *   Authors: NCrashed <ncrashed@gmail.com>
8 */
9 module daemonize.keymap;
10 
11 import std.traits;
12 import std.typetuple;
13 
14 /**
15 *   Simple expression list wrapper.
16 *
17 *   See_Also: Expression list at dlang.org documentation.
18 */
19 template ExpressionList(T...)
20 {
21     alias ExpressionList = T;
22 }
23 /// Example
24 unittest
25 {
26     static assert([ExpressionList!(1, 2, 3)] == [1, 2, 3]);
27 }
28 
29 /**
30 *   Sometimes we don't want to auto expand expression ExpressionLists.
31 *   That can be used to pass several lists into templates without
32 *   breaking their boundaries.
33 */
34 template StrictExpressionList(T...)
35 {
36     alias expand = T;
37 }
38 /// Example
39 unittest
40 {
41     template Test(alias T1, alias T2)
42     {
43         static assert([T1.expand] == [1, 2]);
44         static assert([T2.expand] == [3, 4]);
45         enum Test = true;
46     }
47     
48     static assert(Test!(StrictExpressionList!(1, 2), StrictExpressionList!(3, 4)));
49 }
50 
51 /**
52 *   Same as std.typetyple.staticMap, but passes two arguments to the first template.
53 */
54 template staticMap2(alias F, T...)
55 {
56     static assert(T.length % 2 == 0);
57     
58     static if (T.length < 2)
59     {
60         alias staticMap2 = ExpressionList!();
61     }
62     else static if (T.length == 2)
63     {
64         alias staticMap2 = ExpressionList!(F!(T[0], T[1]));
65     }
66     else
67     {
68         alias staticMap2 = ExpressionList!(F!(T[0], T[1]), staticMap2!(F, T[2  .. $]));
69     }
70 }
71 /// Example
72 unittest
73 {
74     template Test(T...)
75     {
76         enum Test = T[0] && T[1];
77     }
78     
79     static assert([staticMap2!(Test, true, true, true, false)] == [true, false]);
80 }
81 
82 /**
83 *   Performs filtering of expression tuple $(B T) one by one by function or template $(B F). If $(B F)
84 *   returns $(B true) the resulted element goes to returned expression tuple, else it is discarded.
85 */
86 template staticFilter(alias F, T...)
87 {
88     static if(T.length == 0)
89     {
90         alias staticFilter = ExpressionList!();
91     }
92     else
93     {
94     	static if(__traits(compiles, F(T[0]))) enum result = F(T[0]);
95     	else enum result = F!(T[0]);
96     	
97         static if(result)
98         {
99             alias staticFilter = ExpressionList!(T[0], staticFilter!(F, T[1 .. $]));
100         } 
101         else
102         {
103             alias staticFilter = ExpressionList!(staticFilter!(F, T[1 .. $]));
104         }
105     }
106 }
107 /// Example
108 unittest
109 {
110     import std.conv;
111     
112     bool testFunc(int val)
113     { 
114         return val <= 15;
115     }
116     
117     static assert(staticFilter!(testFunc, ExpressionList!(42, 108, 15, 2)) == ExpressionList!(15, 2));
118 }
119 
120 /**
121 *   Performs filtering of expression tuple $(B T) by pairs by function or template $(B F). If $(B F)
122 *   returns $(B true) the resulted pair goes to returned expression tuple, else it is discarded.
123 */
124 template staticFilter2(alias F, T...)
125 {
126     static assert(T.length % 2 == 0);
127     
128     static if (T.length < 2)
129     {
130         alias staticFilter2 = ExpressionList!();
131     }
132     else
133     {
134         static if(F(T[0], T[1]))
135         {
136             alias staticFilter2 = ExpressionList!(T[0], T[1], staticFilter2!(F, T[2 .. $]));
137         }
138         else
139         {
140             alias staticFilter2 = ExpressionList!(staticFilter2!(F, T[2 .. $]));
141         }
142     }
143 }
144 /// Example
145 unittest
146 {
147     import std.conv;
148     
149     bool testFunc(string val1, int val2)
150     { 
151         return val1.to!int == val2;
152     }
153     
154     static assert(staticFilter2!(testFunc, ExpressionList!("42", 42, "2", 108, "15", 15, "1", 2)) == ExpressionList!("42", 42, "15", 15));
155 }
156 
157 /**
158 *   Static version of std.algorithm.reduce (or fold). Expects that $(B F)
159 *   takes accumulator as first argument and a value as second argument.
160 *
161 *   First value of $(B T) have to be a initial value of accumulator.
162 */
163 template staticFold(alias F, T...)
164 {
165     static if(T.length == 0) // invalid input
166     {
167         alias staticFold = ExpressionList!(); 
168     }
169     else static if(T.length == 1)
170     {
171         static if(is(T[0]) || !__traits(compiles, {enum staticFold = T[0];}))
172             alias staticFold = T[0];
173         else
174             enum staticFold = T[0];
175     }
176     else 
177     {
178         alias staticFold = staticFold!(F, F!(T[0], T[1]), T[2 .. $]);
179     }
180 }
181 /// Example
182 unittest
183 {
184     template summ(T...)
185     {
186         enum summ = T[0] + T[1];
187     }
188     
189     static assert(staticFold!(summ, 0, 1, 2, 3, 4) == 10);
190     
191     template preferString(T...)
192     {
193         static if(is(T[0] == string))
194             alias preferString = T[0];
195         else
196             alias preferString = T[1];
197     }
198     
199     static assert(is(staticFold!(preferString, void, int, string, bool) == string));
200     static assert(is(staticFold!(preferString, void, int, double, bool) == bool));
201 }
202 
203 /**
204 *   Compile-time variant of std.range.robin for expression ExpressionLists.
205 *   
206 *   Template expects $(B StrictExpressionList) list as parameter and returns
207 *   new expression list where first element is from first expression ExpressionList,
208 *   second element is from second ExpressionList and so on, until one of input ExpressionLists
209 *   doesn't end.
210 */
211 template staticRobin(SF...)
212 {
213     // Calculating minimum length of all ExpressionLists
214     private static template minimum(T...)
215     {
216         enum length = T[1].expand.length;
217         enum minimum = T[0] > length ? length : T[0];
218     }
219     
220     enum minLength = staticFold!(minimum, size_t.max, SF);
221     
222     private static template robin(ulong i)
223     {        
224         private template takeByIndex(alias T)
225         {
226             static if(is(T.expand[i]))
227                 alias takeByIndex = T.expand[i];
228             else
229             {
230                 // hack to avoid compile-time lambdas
231                 // see http://forum.dlang.org/thread/lkl0lp$204h$1@digitalmars.com
232                 static if(__traits(compiles, {enum takeByIndex = T.expand[i];}))
233                 {
234                     enum takeByIndex = T.expand[i];
235                 }
236                 else
237                 {
238                     alias takeByIndex = T.expand[i];
239                 }
240             }
241         }
242         
243         static if(i >= minLength)
244         {
245             alias robin = ExpressionList!();
246         }
247         else
248         {
249             alias robin = ExpressionList!(staticMap!(takeByIndex, SF), robin!(i+1));
250         }
251     }
252     
253     alias staticRobin = robin!0; 
254 }
255 /// Example
256 unittest
257 {
258     alias test = staticRobin!(StrictExpressionList!(int, int, int), StrictExpressionList!(float, float));
259     static assert(is(test == ExpressionList!(int, float, int, float)));
260     
261     alias test2 = staticRobin!(StrictExpressionList!(1, 2), StrictExpressionList!(3, 4, 5), StrictExpressionList!(6, 7));
262     static assert([test2]== [1, 3, 6, 2, 4, 7]);
263 }
264 
265 /**
266 *   Static associative map.
267 *
268 *   $(B Pairs) is a list of pairs key-value.
269 */
270 template KeyValueList(Pairs...)
271 {
272     static assert(Pairs.length % 2 == 0, text("KeyValueList is expecting even count of elements, not ", Pairs.length));
273     
274     /// Number of entries in the map
275     enum length = Pairs.length / 2;
276     
277     /**
278     *   Getting values by keys. If $(B Keys) is a one key, then
279     *   returns unwrapped value, else a ExpressionExpressionList of values.
280     */
281     template get(Keys...)
282     {
283         static assert(Keys.length > 0, "KeyValueList.get is expecting an argument!");
284         static if(Keys.length == 1)
285         {
286             static if(is(Keys[0]) || !__traits(compiles, { enum Key = Keys[0]; }) ) { 
287                 alias Key = Keys[0];
288             } else {
289                 enum Key = Keys[0];
290                 static assert(__traits(compiles, Key == Key), text(typeof(Key).stringof, " must have a opEqual!"));
291             }
292             
293             private static template innerFind(T...)
294             {
295                 static if(T.length == 0) {
296                     alias innerFind = ExpressionList!();
297                 } else
298                 {
299                     static if(is(Keys[0])) { 
300                         static if(is(T[0] == Key)) {
301                             static if(is(T[1])) {
302                                 alias innerFind = T[1];
303                             } else {
304                                 enum innerFind = T[1];
305                             }
306                         } else {
307                             alias innerFind = innerFind!(T[2 .. $]);
308                         }
309                     } else
310                     {
311                     	static if(__traits(compiles, T[0].opEqual!(Key))) enum cmpRes = T[0].opEqual!(Key);
312                     	else enum cmpRes = T[0] == Key;
313                     	 
314                         static if(cmpRes) {
315                             static if(is(T[1])) {
316                                 alias innerFind = T[1];
317                             } else {
318                                 // hack to avoid compile-time lambdas
319                                 // see http://forum.dlang.org/thread/lkl0lp$204h$1@digitalmars.com
320                                 static if(__traits(compiles, {enum innerFind = T[1];}))
321                                 {
322                                     enum innerFind = T[1];
323                                 } else
324                                 {
325                                     alias innerFind = T[1];
326                                 }
327                             }
328                         } else {
329                             alias innerFind = innerFind!(T[2 .. $]);
330                         }
331                     }
332                 }
333             }
334 
335             alias get = innerFind!Pairs; 
336         } else {
337             alias get = ExpressionList!(get!(Keys[0 .. $/2]), get!(Keys[$/2 .. $]));
338         }
339     }
340     
341     /// Returns true if map has a $(B Key)
342     template has(Key...)
343     {
344         static assert(Key.length == 1);
345         enum has = ExpressionList!(get!Key).length > 0; 
346     }
347     
348     /// Setting values to specific keys (or adding new key-values)
349     template set(KeyValues...)
350     {
351         static assert(KeyValues.length >= 2, "KeyValueList.set is expecting at least one pair!");
352         static assert(KeyValues.length % 2 == 0, "KeyValuesExpressionList.set is expecting even count of arguments!");
353         
354         template inner(KeyValues...)
355         {
356             static if(KeyValues.length == 2) {
357                 static if(is(KeyValues[0])) {
358                     alias Key = KeyValues[0];
359                 } else {
360                     enum Key = KeyValues[0];
361                 }
362                 
363                 static if(is(KeyValues[1])) {
364                     alias Value = KeyValues[1];
365                 } else {
366                     enum Value = KeyValues[1];
367                 }
368                 
369                 private template innerFind(T...)
370                 {
371                     static if(T.length == 0) {
372                         alias innerFind = ExpressionList!(Key, Value);
373                     } else
374                     {
375                         static if(is(Key)) { 
376                             static if(is(T[0] == Key)) {
377                                 alias innerFind = ExpressionList!(Key, Value, T[2 .. $]);
378                             } else {
379                                 alias innerFind = ExpressionList!(T[0 .. 2], innerFind!(T[2 .. $]));
380                             }
381                         } else
382                         {
383                             static if(T[0] == Key) {
384                                 alias innerFind = ExpressionList!(Key, Value, T[2 .. $]);
385                             } else {
386                                 alias innerFind = ExpressionList!(T[0 .. 2], innerFind!(T[2 .. $]));
387                             }
388                         }
389                     }
390                 }
391     
392                 alias inner = innerFind!Pairs; 
393             } else {
394                 alias inner = ExpressionList!(inner!(KeyValues[0 .. $/2]), inner!(KeyValues[$/2 .. $]));
395             }
396         }
397         alias set = KeyValueList!(inner!KeyValues);
398     }
399     
400     /// Applies $(B F) template for each pair (key-value).
401     template map(alias F)
402     {
403         alias map = KeyValueList!(staticMap2!(F, Pairs));
404     }
405     
406     private static template getKeys(T...)
407     {
408         static if(T.length == 0) {
409             alias getKeys = ExpressionList!();
410         } else {
411             alias getKeys = ExpressionList!(T[0], getKeys!(T[2 .. $]));
412         }
413     }
414     /// Getting expression list of all keys
415     alias keys = getKeys!Pairs;
416     
417     private static template getValues(T...)
418     {
419         static if(T.length == 0) {
420             alias getValues = ExpressionList!();
421         } else {
422             alias getValues = ExpressionList!(T[1], getValues!(T[2 .. $]));
423         }
424     }
425     /// Getting expression list of all values
426     alias values = getValues!Pairs;
427     
428     /** 
429     *   Filters entries with function or template $(B F), leaving entry only if
430     *   $(B F) returning $(B true).
431     */
432     static template filter(alias F)
433     {
434         alias filter = KeyValueList!(staticFilter2!(F, Pairs));
435     } 
436     
437     /** 
438     *   Filters entries with function or template $(B F) passing only a key from an entry, leaving entry only if
439     *   $(B F) returning $(B true).
440     */
441     static template filterByKey(alias F)
442     {
443         private alias newKeys = staticFilter!(F, keys);
444         private alias newValues = staticMap!(get, newKeys);
445         alias filterByKey = KeyValueList!(staticRobin!(StrictExpressionList!newKeys, StrictExpressionList!newValues));
446     }
447 }
448 ///
449 unittest
450 {
451     alias map = KeyValueList!("a", 42, "b", 23);
452     static assert(map.get!"a" == 42);
453     static assert(map.get!("a", "b") == ExpressionList!(42, 23));
454     static assert(map.get!"c".length == 0);
455     
456     alias map2 = KeyValueList!(int, float, float, double, double, 42);
457     static assert(is(map2.get!int == float));
458     static assert(is(map2.get!float == double));
459     static assert(map2.get!double == 42); 
460     
461     static assert(map.has!"a");
462     static assert(map2.has!int);
463     static assert(!map2.has!void);
464     static assert(!map.has!"c");
465     
466     alias map3 = map.set!("c", 4);
467     static assert(map3.get!"c" == 4);
468     alias map4 = map.set!("c", 4, "d", 8);
469     static assert(map4.get!("c", "d") == ExpressionList!(4, 8));
470     alias map5 = map.set!("a", 4);
471     static assert(map5.get!"a" == 4);
472     
473     template inc(string key, int val)
474     {
475         alias inc = ExpressionList!(key, val+1);
476     }
477     
478     alias map6 = map.map!inc;
479     static assert(map6.get!"a" == 43);
480     static assert(map6.get!("a", "b") == ExpressionList!(43, 24));
481     
482     static assert(map.keys == ExpressionList!("a", "b"));
483     static assert(map.values == ExpressionList!(42, 23));
484 }