1 module mage.log;
2 import mage;
3 
4 import io = std.stdio;
5 
6 
7 struct Block
8 {
9   string message;
10   int indentation;
11   bool isPrinted = false;
12 
13   @disable this();
14 
15   this(Args...)(string message, Args fmtargs)
16   {
17     static if(Args.length) {
18       this.message = message.format(fmtargs);
19     }
20     else {
21       this.message = message;
22     }
23     this.indentation = blocks.length;
24     blocks ~= &this;
25   }
26 
27   ~this()
28   {
29     assert(blocks[$-1] == &this, "Popping log Blocks in wrong order.");
30     blocks.length--;
31     if(this.isPrinted) {
32       print("<<<| ", "<<< ", "");
33     }
34   }
35 
36   void print(string linePrefix, string messagePrefix, string messageSuffix)
37   {
38     io.writef("%-*s", 2 * this.indentation + linePrefix.length, linePrefix);
39     io.writefln("%s%s%s", messagePrefix, this.message, messageSuffix);
40     this.isPrinted = true;
41   }
42 }
43 
44 auto forcedBlock(Args...)(Args args)
45 {
46   auto b = Block(args);
47   printBlocks();
48   return b;
49 }
50 
51 package
52 {
53   Block*[] blocks;
54 
55   void printBlocks()
56   {
57     foreach(block; blocks) {
58       if(!block.isPrinted) {
59         block.print(">>>| ", ">>> ", "");
60         assert(block.isPrinted);
61       }
62     }
63   }
64 
65   void doLog(string prefix, Args...)(string message, Args fmtargs)
66   {
67     printBlocks();
68     io.write(indentString(prefix));
69     io.writefln(message, fmtargs);
70   }
71 }
72 
73 @property int indentSize() { return blocks.length; }
74 string indentString(string prefix = "") { return "%-*s".format(2 * indentSize + prefix.length, prefix); }
75 
76 void info   (Args...)(string fmt, Args fmtargs) { doLog!"Ifo| "(fmt, fmtargs); }
77 void trace  (Args...)(string fmt, Args fmtargs) { doLog!"Trc| "(fmt, fmtargs); }
78 void error  (Args...)(string fmt, Args fmtargs) { doLog!"Err| "(fmt, fmtargs); }
79 void warning(Args...)(string fmt, Args fmtargs) { doLog!"Wrn| "(fmt, fmtargs); }
80 
81 unittest
82 {
83   info("Hello testing world");
84   with(Block("Block 0"))
85   {
86     info("Inner message");
87     with(Block("Block 1"))
88     {
89       info("Going deeper...");
90     }
91     info("Backing out again.");
92   }
93   info("Back to the top-level");
94 }