1 module colored;
2 
3 import std.string;
4 
5 enum AnsiColor
6 {
7     black = 30,
8     red = 31,
9     green = 32,
10     yellow = 33,
11     blue = 34,
12     magenta = 35,
13     cyan = 36,
14     white = 37,
15     defaultColor = 39
16 }
17 
18 struct StringWithForeground(T)
19 {
20     string s;
21     T fg;
22     this(string s, T fg)
23     {
24         this.s = s;
25         this.fg = fg;
26     }
27 
28     string toString()
29     {
30         return "\033[%dm%s\033[0m".format(fg, s);
31     }
32 }
33 
34 struct StringWithBackground(T)
35 {
36     string s;
37     T bg;
38     this(string s, T bg)
39     {
40         this.s = s;
41         this.bg = bg;
42     }
43 
44     string toString()
45     {
46         return "\033[%dm%s\033[0m".format(bg + 10, s);
47     }
48 }
49 
50 struct StringWithBoth(T)
51 {
52     string s;
53     T fg;
54     T bg;
55     this(string s, T fg, T bg)
56     {
57         this.s = s;
58         this.fg = fg;
59         this.bg = bg;
60     }
61 
62     string toString()
63     {
64         return "\033[%dm\033[%dm%s\033[0m".format(fg, bg + 10, s);
65     }
66 }
67 
68 @("color structs") unittest
69 {
70     import unit_threaded;
71 
72     StringWithForeground!AnsiColor("fgTest", AnsiColor.red).toString.shouldEqual(
73             "\033[31mfgTest\033[0m");
74     StringWithBackground!AnsiColor("bgTest", AnsiColor.red).toString.shouldEqual(
75             "\033[41mbgTest\033[0m");
76     StringWithBoth!AnsiColor("bothTest", AnsiColor.red, AnsiColor.red).toString.shouldEqual(
77             "\033[31m\033[41mbothTest\033[0m");
78 }
79 
80 string asMixin(T)()
81 {
82     import std.conv;
83     import std.traits;
84 
85     string res = "";
86     foreach (immutable ansiColor; [EnumMembers!T])
87     {
88         res ~= "auto %1$s(string s) { return StringWithForeground!%2$s(s, %2$s.%1$s); }\n".format(ansiColor,
89                 typeof(T.init).stringof);
90         res ~= "auto %1$s(StringWithBackground!%2$s s) { return StringWithBoth!%2$s(s.s, %2$s.%1$s, s.bg); }\n".format(
91                 ansiColor, typeof(T.init).stringof);
92         string n = ansiColor.to!string;
93         string name = n[0 .. 1].toUpper ~ n[1 .. $];
94         res ~= "auto on%3$s(string s) { return StringWithBackground!%2$s(s, %2$s.%1$s); }\n".format(ansiColor,
95                 typeof(T.init).stringof, name);
96         res ~= "auto on%3$s(StringWithForeground!%2$s s) { return StringWithBoth!%2$s(s.s, s.fg, %2$s.%1$s); }\n"
97             .format(ansiColor, typeof(T.init).stringof, name);
98     }
99     return res;
100 }
101 
102 @("color mixins") unittest
103 {
104     import unit_threaded;
105 
106     enum TTT
107     {
108         r = 1
109     }
110 
111     asMixin!TTT.shouldEqual(`auto r(string s) { return StringWithForeground!TTT(s, TTT.r); }
112 auto r(StringWithBackground!TTT s) { return StringWithBoth!TTT(s.s, TTT.r, s.bg); }
113 auto onR(string s) { return StringWithBackground!TTT(s, TTT.r); }
114 auto onR(StringWithForeground!TTT s) { return StringWithBoth!TTT(s.s, s.fg, TTT.r); }
115 `);
116 }
117 
118 mixin(asMixin!AnsiColor);
119 
120 unittest
121 {
122     import std.stdio;
123 
124     writeln("Hello World".black);
125     writeln("Hello World".red);
126     writeln("Hello World".green);
127     writeln("Hello World".yellow);
128     writeln("Hello World".blue);
129     writeln("Hello World".magenta);
130     writeln("Hello World".cyan);
131     writeln("Hello World".white);
132     writeln("Hello World".defaultColor);
133 }