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     lightGray = 37,
15     defaultColor = 39,
16     darkGray = 90,
17     lightRed = 91,
18     lightGreen = 92,
19     lightYellow = 93,
20     lightBlue = 94,
21     lightMagenta = 95,
22     lightCyan = 96,
23     white = 97
24 }
25 
26 enum Style
27 {
28     bold = 1,
29     dim = 2,
30     underlined = 4,
31     blink = 5,
32     reverse = 7,
33     hidden = 8
34 }
35 
36 struct StyledString
37 {
38     string s;
39     int[] befores;
40     int[] afters;
41     this(string s)
42     {
43         this.s = s;
44     }
45 
46     private StyledString addPair(int before, int after)
47     {
48         befores ~= before;
49         afters ~= after;
50         return this;
51     }
52 
53   StyledString setForeground(int color) {
54     return addPair(color, 0);
55   }
56   StyledString setBackground(int color) {
57     return addPair(color + 10, 0);
58   }
59   StyledString addStyle(int style) {
60     return addPair(style, style+20);
61   }
62     string toString()
63     {
64         import std.algorithm;
65 
66         auto prefix = befores.map!(a => "\033[%dm".format(a)).join("");
67         auto suffix = afters.map!(a => "\033[%dm".format(a)).join("");
68         return "%s%s%s".format(prefix, s, suffix);
69     }
70 }
71 
72 @("styledstring") unittest
73 {
74     import unit_threaded;
75     import std.stdio;
76     import std.traits;
77 
78     foreach (immutable color; [EnumMembers!AnsiColor])
79     {
80         auto colorName = "%s".format(color);
81         writeln(StyledString(colorName).setForeground(color));
82     }
83     foreach (immutable color; [EnumMembers!AnsiColor])
84     {
85         auto colorName = "bg%s".format(color);
86         writeln(StyledString(colorName).setBackground(color));
87     }
88     foreach (immutable style; [EnumMembers!Style])
89     {
90         auto styleName = "%s".format(style);
91         writeln(StyledString(styleName).addStyle(style));
92     }
93 }
94 
95 auto colorMixin(T)()
96 {
97     import std.traits;
98 
99     string res = "";
100     foreach (immutable color; [EnumMembers!T])
101     {
102         auto t = typeof(T.init).stringof;
103         auto c = "%s".format(color);
104         res ~= "auto %1$s(string s) { return StyledString(s).setForeground(%2$s.%1$s); }\n".format(c,
105                 t);
106         res ~= "auto %1$s(StyledString s) { return s.setForeground(%2$s.%1$s); }\n".format(c, t);
107         string name = c[0 .. 1].toUpper ~ c[1 .. $];
108         res ~= "auto on%3$s(string s) { return StyledString(s).setBackground(%2$s.%1$s); }\n".format(c,
109                 t, name);
110         res ~= "auto on%3$s(StyledString s) { return s.setBackground(%2$s.%1$s); }\n".format(c,
111                 t, name);
112     }
113     return res;
114 }
115 
116 auto styleMixin(T)()
117 {
118     import std.traits;
119 
120     string res = "";
121     foreach (immutable style; [EnumMembers!T])
122     {
123         auto t = typeof(T.init).stringof;
124         auto s = "%s".format(style);
125         res ~= "auto %1$s(string s) { return StyledString(s).addStyle(%2$s.%1$s); }\n".format(s,
126                 t);
127         res ~= "auto %1$s(StyledString s) { return s.addStyle(%2$s.%1$s); }\n".format(s,
128                 t);
129     }
130     return res;
131 }
132 
133 mixin(colorMixin!AnsiColor);
134 mixin(styleMixin!Style);
135 
136 @("api") unittest
137 {
138     import std.stdio;
139 
140     writeln("red".red.onGreen);
141     writeln("red".red.onYellow.bold.underlined);
142 }