1 /*******************************************************************************
2 
3     Utilities used internally by the config parser.
4 
5     Compile this library with `-debug=ConfigFillerDebug` to get verbose output.
6     This can be achieved with `debugVersions` in dub, or by depending on the
7     `debug` configuration provided by `dub.json`.
8 
9     Copyright:
10         Copyright (c) 2019-2022 BOSAGORA Foundation
11         All rights reserved.
12 
13     License:
14         MIT License. See LICENSE for details.
15 
16 *******************************************************************************/
17 
18 module configy.Utils;
19 
20 import std.format;
21 
22 /// Type of sink used by the `toString`
23 package alias SinkType = void delegate (in char[]) @safe;
24 
25 /*******************************************************************************
26 
27     Debugging utility for config filler
28 
29     Since this module does a lot of meta-programming, some things can easily
30     go wrong. For example, a condition being false might happen because it is
31     genuinely false or because the condition is buggy.
32 
33     To make figuring out if a config is properly parsed or not, a little utility
34     (config-dumper) exists, which will provide a verbose output of what the
35     config filler does. To do this, `config-dumper` is compiled with
36     the below `debug` version.
37 
38 *******************************************************************************/
39 
40 debug (ConfigFillerDebug)
41 {
42     /// A thin wrapper around `stderr.writefln` with indentation
43     package void dbgWrite (Args...) (string fmt, Args args)
44     {
45         import std.stdio;
46         stderr.write(IndentChars[0 .. indent >= IndentChars.length ? $ : indent]);
47         stderr.writefln(fmt, args);
48     }
49 
50     /// Log a value that is to be returned
51     /// The value will be the first argument and painted yellow
52     package T dbgWriteRet (T, Args...) (auto ref T return_, string fmt, Args args)
53     {
54         dbgWrite(fmt, return_.paint(Yellow), args);
55         return return_;
56     }
57 
58     /// The current indentation
59     package size_t indent;
60 
61     /// Helper for indentation (who needs more than 16 levels of indent?)
62     private immutable IndentChars = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
63 }
64 else
65 {
66     /// No-op
67     package void dbgWrite (Args...) (string fmt, lazy Args args) {}
68 
69     /// Ditto
70     package T dbgWriteRet (T, Args...) (auto ref T return_, string fmt, lazy Args args)
71     {
72         return return_;
73     }
74 }
75 
76 /// Thin wrapper to simplify colorization
77 package struct Colored (T)
78 {
79     /// Color used
80     private string color;
81 
82     /// Value to print
83     private T value;
84 
85     /// Hook for `formattedWrite`
86     public void toString (scope SinkType sink)
87     {
88         static if (is(typeof(T.init.length) : size_t))
89             if (this.value.length == 0) return;
90 
91         formattedWrite(sink, "%s%s%s", this.color, this.value, Reset);
92     }
93 }
94 
95 /// Ditto
96 package Colored!T paint (T) (T arg, string color)
97 {
98     return Colored!T(color, arg);
99 }
100 
101 /// Paint `arg` in color `ifTrue` if `cond` evaluates to `true`, use color `ifFalse` otherwise
102 package Colored!T paintIf (T) (T arg, bool cond, string ifTrue, string ifFalse)
103 {
104     return Colored!T(cond ? ifTrue : ifFalse, arg);
105 }
106 
107 /// Paint a boolean in green if `true`, red otherwise, unless `reverse` is set to `true`,
108 /// in which case the colors are swapped
109 package Colored!bool paintBool (bool value, bool reverse = false)
110 {
111     return value.paintIf(reverse ^ value, Green, Red);
112 }
113 
114 /// Reset the foreground color used
115 package immutable Reset = "\u001b[0m";
116 /// Set the foreground color to red, used for `false`, missing, errors, etc...
117 package immutable Red = "\u001b[31m";
118 /// Set the foreground color to red, used for warnings and other things
119 /// that should draw attention but do not pose an immediate issue
120 package immutable Yellow = "\u001b[33m";
121 /// Set the foreground color to green, used for `true`, present, etc...
122 package immutable Green = "\u001b[32m";
123 /// Set the foreground color to green, used field names / path
124 package immutable Cyan = "\u001b[36m";