1 module colored;
2 
3 import std.string;
4 
5 enum AnsiColor {
6   black = 30,
7   red = 31,
8   green = 32,
9   yellow = 33,
10   blue = 34,
11   magenta = 35,
12   cyan = 36,
13   white = 37
14 }
15 
16 struct StringWithForeground(T) {
17   string s;
18   T fg;
19   this(string s, T fg) {
20     this.s = s;
21     this.fg = fg;
22   }
23   string toString() {
24     return "\033[%dm%s\033[0m".format(fg, s);
25   }
26 }
27 
28 struct StringWithBackground(T) {
29   string s;
30   T bg;
31   this(string s, T bg) {
32     this.s = s;
33     this.bg = bg;
34   }
35   string toString() {
36     return "\033[%dm%s\033[0m".format(bg+10, s);
37   }
38 }
39 
40 struct StringWithBoth(T) {
41   string s;
42   T fg;
43   T bg;
44   this(string s, T fg, T bg) {
45     this.s = s;
46     this.fg = fg;
47     this.bg = bg;
48   }
49   string toString() {
50     return "\033[%dm\033[%dm%s\033[0m".format(fg, bg+10, s);
51   }
52 }
53 
54 @("color structs") unittest {
55   import unit_threaded;
56   StringWithForeground!AnsiColor("fgTest", AnsiColor.red).toString.shouldEqual("\033[31mfgTest\033[0m");
57   StringWithBackground!AnsiColor("bgTest", AnsiColor.red).toString.shouldEqual("\033[41mbgTest\033[0m");
58   StringWithBoth!AnsiColor("bothTest", AnsiColor.red, AnsiColor.red).toString.shouldEqual("\033[31m\033[41mbothTest\033[0m");
59 }
60 
61 string asMixin(T)() {
62   import std.conv;
63   import std.traits;
64   string res = "";
65   foreach (immutable ansiColor; [EnumMembers!T]) {
66     res ~= "auto %1$s(string s) { return StringWithForeground!%2$s(s, %2$s.%1$s); }\n".format(ansiColor, typeof(T.init).stringof);
67     res ~= "auto %1$s(StringWithBackground!%2$s s) { return StringWithBoth!%2$s(s.s, %2$s.%1$s, s.bg); }\n".format(ansiColor, typeof(T.init).stringof);
68     string n = ansiColor.to!string;
69     string name = n[0..1].toUpper ~ n[1..$];
70     res ~= "auto on%3$s(string s) { return StringWithBackground!%2$s(s, %2$s.%1$s); }\n".format(ansiColor, typeof(T.init).stringof, name);
71     res ~= "auto on%3$s(StringWithForeground!%2$s s) { return StringWithBoth!%2$s(s.s, s.fg, %2$s.%1$s); }\n".format(ansiColor, typeof(T.init).stringof, name);
72   }
73   return res;
74 }
75 
76 @("color mixins") unittest {
77   import unit_threaded;
78   enum TTT {
79     r = 1
80   }
81   asMixin!TTT.shouldEqual(
82 `auto r(string s) { return StringWithForeground!TTT(s, TTT.r); }
83 auto r(StringWithBackground!TTT s) { return StringWithBoth!TTT(s.s, TTT.r, s.bg); }
84 auto onR(string s) { return StringWithBackground!TTT(s, TTT.r); }
85 auto onR(StringWithForeground!TTT s) { return StringWithBoth!TTT(s.s, s.fg, TTT.r); }
86 `);
87 }
88 
89 mixin(asMixin!AnsiColor);