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 	@property string toString() pure nothrow @safe @nogc => toStringFromEnumWithConsecutiveAliases(_enum);
15 	E _enum;					// the wrapped enum
16 	alias _enum this;
17 }
18 
19 ///
20 pure @safe unittest {
21 	enum X { a,
22 			 b,
23 	}
24 	alias EnumX = Enum!X;
25 	assert(EnumX(X.a).toString == "a");
26 	assert(EnumX(X.b).toString == "b");
27 }
28 
29 /** Fast and more generic implementation of `std.conv.to` for enumerations.
30 	TODO: Handle non-adjacent enumerator aliases.
31  */
32 string toStringFromEnumWithConsecutiveAliases(T)(const scope T value) pure nothrow @safe @nogc
33 if (is(T == enum)) {
34 	alias members = __traits(allMembers, T);
35 	final switch (value) {
36 		static foreach (index, member; members) {
37 			static if (index == 0 ||
38 					   (__traits(getMember, T, members[index - 1]) !=
39 						__traits(getMember, T, member))) {
40 				case __traits(getMember, T, member):
41 					return member;
42 			}
43 		}
44 	}
45 }
46 
47 ///
48 pure nothrow @safe @nogc unittest {
49 	enum E { unknown, x, y, z, z_ = z, }
50 	assert(E.x.toStringFromEnumWithConsecutiveAliases == "x");
51 	assert(E.y.toStringFromEnumWithConsecutiveAliases == "y");
52 	assert(E.z.toStringFromEnumWithConsecutiveAliases == "z");
53 	assert(E.z_.toStringFromEnumWithConsecutiveAliases == "z");
54 }
55 
56 /** Faster implementation of `std.conv.to` for enumerations with no aliases.
57 	Will error if aliases are present.
58  */
59 string toStringFromEnumWithNoAliases(T)(const scope T value) pure nothrow @safe @nogc
60 if (is(T == enum)) /+ TODO: check for no aliases +/ {
61 	final switch (value) {
62 		static foreach (member; __traits(allMembers, T)) {
63 		case __traits(getMember, T, member):
64 			return member;
65 		}
66 	}
67 }
68 
69 ///
70 pure nothrow @safe @nogc unittest {
71 	enum E { unknown, x, y, z, }
72 	assert(E.x.toStringFromEnumWithNoAliases == "x");
73 	assert(E.y.toStringFromEnumWithNoAliases == "y");
74 	assert(E.z.toStringFromEnumWithNoAliases == "z");
75 }
76 
77 /** Convert enumerator value `v` to `string`.
78 	See_Also: http://forum.dlang.org/post/aqqhlbaepoimpopvouwv@forum.dlang.org
79  */
80 string toStringFromEnumThatMimicsPhobos(T)(T v) if (is(T == enum)) {
81 	switch (v) {
82 		foreach (m; __traits(allMembers, T)) {
83 			case mixin("T." ~ m) : return m;
84 		}
85 		default: {
86 			char[] result = ("cast(" ~ T.stringof ~ ")").dup;
87 			uint val = v;
88 
89 			enum headLength = T.stringof.length + "cast()".length;
90 			const uint log10Val = (val < 10) ? 0 : (val < 100) ? 1 : (val < 1_000) ? 2 :
91 				(val < 10_000) ? 3 : (val < 100_000) ? 4 : (val < 1_000_000) ? 5 :
92 				(val < 10_000_000) ? 6 : (val < 100_000_000) ? 7 : (val < 1000_000_000) ? 8 : 9;
93 
94 			result.length += log10Val + 1;
95 
96 			foreach (uint i; 0 .. log10Val + 1) {
97 				cast(char)result[headLength + log10Val - i] = cast(char) ('0' + (val % 10));
98 				val /= 10;
99 			}
100 			return () @trusted { return cast(string) result; }();
101 		}
102 	}
103 }
104 
105 pure nothrow @safe unittest {
106 	enum ET { one, two }
107 	// static assert(to!string(ET.one) == "one");
108 	static assert (toStringFromEnumThatMimicsPhobos(ET.one) == "one");
109 	assert (toStringFromEnumThatMimicsPhobos(ET.one) == "one");
110 }