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