1 module nxt.enum_ex;
2 
3 @safe:
4 
5 /** Enumeration wrapper that uses optimized conversion to string (via `toString`
6  * member).
7  *
8  * See_Also: https://forum.dlang.org/thread/ppndhxvzayedgpbjculm@forum.dlang.org?page=1
9  *
10  * TODO: Move logic to `std.conv.to`.
11  */
12 struct Enum(E)
13 if (is(E == enum))
14 {
15     @property string toString() @safe pure nothrow @nogc
16     {
17         return toStringFaster(_enum);
18     }
19     E _enum;                    // the wrapped enum
20     alias _enum this;
21 }
22 
23 ///
24 @safe pure unittest
25 {
26     enum X { a,
27              b,
28              _b = b             // enumerator alias
29     }
30     alias EnumX = Enum!X;
31     assert(EnumX(X.a).toString == "a");
32     assert(EnumX(X.b).toString == "b");
33     assert(EnumX(X._b).toString == "b"); // alias encodes to original
34 }
35 
36 /** Fast and more generic implementation of `std.conv.to` for enumerations.
37  */
38 string toStringFaster(T)(const scope T value) @safe pure nothrow @nogc
39 if (is(T == enum))
40 {
41     import std.meta : AliasSeq;
42     /* TODO: skip wrapping in `AliasSeq` when `allMembers` can be iterated
43      * directly when a bug in compiler has been fixed */
44     alias members = AliasSeq!(__traits(allMembers, T));
45     final switch (value)
46     {
47         static foreach (index, member; members)
48         {
49             static if (index == 0 ||
50                        (__traits(getMember, T, members[index - 1]) !=
51                         __traits(getMember, T, member)))
52             {
53             case __traits(getMember, T, member):
54                 return member;
55             }
56         }
57     }
58 }
59 
60 ///
61 @safe pure nothrow @nogc unittest
62 {
63     enum E { unknown, x, y, z, }
64     assert(E.x.toStringFaster == "x");
65     assert(E.y.toStringFaster == "y");
66     assert(E.z.toStringFaster == "z");
67 }
68 
69 /** Faster implementation of `std.conv.to` for enumerations with no aliases.
70  */
71 string toStringNonAliases(T)(const scope T value) @safe pure nothrow @nogc
72 if (is(T == enum))              // TODO: check for no aliases
73 {
74     final switch (value)
75     {
76         static foreach (member; __traits(allMembers, T))
77         {
78         case __traits(getMember, T, member):
79             return member;
80         }
81     }
82 }
83 
84 ///
85 @safe pure nothrow @nogc unittest
86 {
87     enum E { unknown, x, y, z, }
88     assert(E.x.toStringNonAliases == "x");
89     assert(E.y.toStringNonAliases == "y");
90     assert(E.z.toStringNonAliases == "z");
91 }