1 /** 
2  * The custom text transformer that implements the gogga-stylised
3  * logging messages
4  */
5 module gogga.transform;
6 
7 import dlog;
8 import gogga.context;
9 import std.conv : to;
10 
11 /** 
12  * The gogga styles supported
13  */
14 public enum GoggaMode
15 {
16     /** 
17      * TwoKTwenty3 is: `[<file>] (<module>:<lineNumber>) <message>`
18      */
19     TwoKTwenty3,
20 
21     /** 
22      * Simple mode is just: `[<LEVEL>] <message>`
23      */
24     SIMPLE,
25 
26     /** 
27      * Rustacean mode is: `[<LEVEL>] (<filePath>/<functionName>:<lineNumber>) <message>`
28      */
29     RUSTACEAN,
30 
31     /** 
32      * Simple rustacean mode is: `[<LEVEL>] (<functionName>:<lineNumber>) <message>`
33      */
34     RUSTACEAN_SIMPLE
35 }
36 
37 /** 
38  * The custom gogga text transformer
39  */
40 public class GoggaTransform : MessageTransform
41 {
42     /** 
43      * Current style
44      */
45     private GoggaMode mode;
46 
47     /** 
48      * Transforms the provided text
49      *
50      * Params:
51      *   text = text to transform
52      *   ctx = the context passed in
53      * Returns: a string of transformed text
54      */
55     public override string transform(string text, Context ctx)
56     {
57         /* The generated output string */
58         string finalOutput;
59 
60 
61         /* Get the GoggaContext */
62         GoggaContext gCtx = cast(GoggaContext)ctx;
63 
64         /* Extract the line information */
65         CompilationInfo compInfo = gCtx.getLineInfo();
66         string[] context = compInfo.toArray();
67 
68         /* Extract the Level */
69         Level level = gCtx.getLevel();
70 
71 
72         /** 
73          * Simple mode is just: `[<LEVEL>] <message>`
74          */
75         if(mode == GoggaMode.SIMPLE)
76         {
77             finalOutput = cast(string)debugColor("["~to!(string)(level)~"] ", level);
78 
79             finalOutput ~= text~"\n";
80         }
81         /** 
82          * TwoKTwenty3 is: `[<file>] (<module>:<lineNumber>) <message>`
83          */
84         else if(mode == GoggaMode.TwoKTwenty3)
85         {
86             /* Module information (and status debugColoring) */
87             string moduleInfo = cast(string)debugColor("["~context[1]~"]", level);
88             
89             /* Function and line number info */
90             string funcInfo = cast(string)(colorSrc("("~context[4]~":"~context[2]~")"));
91 
92             finalOutput =  moduleInfo~" "~funcInfo~" "~text~"\n";
93         }
94         /** 
95          * Rustacean mode is: `[<LEVEL>] (<filePath>/<functionName>:<lineNumber>) <message>`
96          */
97         else if(mode == GoggaMode.RUSTACEAN)
98         {
99             finalOutput = cast(string)debugColor(to!(string)(level)~"\t", level);
100             finalOutput ~= cast(string)(colorSrc(context[1]~"/"~context[4]~":"~context[2]~"  "));
101             finalOutput ~= text~"\n";
102         }
103         /** 
104          * Simple rustacean mode is: `[<LEVEL>] (<functionName>:<lineNumber>) <message>`
105          */
106         else if(mode == GoggaMode.RUSTACEAN_SIMPLE)
107         {
108             finalOutput = cast(string)debugColor(to!(string)(level)~"\t", level);
109             finalOutput ~= cast(string)(colorSrc(context[4]~":"~context[2]~"  "));
110             finalOutput ~= text~"\n";
111         }
112 
113         return finalOutput;
114     }
115 
116     /** 
117      * Set the gogga style
118      *
119      * Params:
120      *   mode = the GoggaMode to use
121      */
122     public void setMode(GoggaMode mode)
123     {
124         this.mode = mode;
125     }
126 }
127 
128 /** 
129  * Colorise the text provided accoridng to the level and then
130  * reset the colors at the end
131  *
132  * Params:
133  *   text = the text to colorise
134  *   level = the color to use
135  * Returns: the byte sequence of characters and controls
136  */
137 private byte[] debugColor(string text, Level level)
138 {
139     /* The generated message */
140     byte[] messageBytes;
141 
142     /* If INFO, set green */
143     if(level == Level.INFO)
144     {
145         messageBytes = cast(byte[])[27, '[','3','2','m'];
146     }
147     /* If WARN, set yellow */
148     else if(level == Level.WARN)
149     {
150         messageBytes = cast(byte[])[27, '[','3','1', ';', '9', '3', 'm'];
151     }
152     /* If ERROR, set red */
153     else if(level == Level.ERROR)
154     {
155         messageBytes = cast(byte[])[27, '[','3','1','m'];
156     }
157     /* If DEBUG, set pink */
158     else
159     {
160         messageBytes = cast(byte[])[27, '[','3','5','m'];
161     }
162 
163     /* Add the message */
164     messageBytes ~= cast(byte[])text;
165 
166     /* Switch back debugColor */
167     messageBytes ~= cast(byte[])[27, '[', '3', '9', 'm'];
168 
169     /* Reset coloring */
170     messageBytes ~= [27, '[', 'm'];
171 
172     return messageBytes;
173 }
174 
175 /** 
176  * Colors the provided text in a gray fashion and then
177  * resets back to normal
178  *
179  * Params:
180  *   text = the text to gray color
181  * Returns: the byte sequence of characters and controls
182  */
183 private byte[] colorSrc(string text)
184 {
185     /* The generated message */
186     byte[] messageBytes;
187 
188     /* Reset coloring */
189     messageBytes ~= [27, '[', 'm'];
190 
191     /* Color gray */
192     messageBytes ~= [27, '[', '3', '9', ';', '2', 'm'];
193 
194     /* Append the message */
195     messageBytes ~= text;
196 
197     /* Reset coloring */
198     messageBytes ~= [27, '[', 'm'];
199 
200     return messageBytes;
201 }