1 /* 2 Very basic table<nested table> implementation. 3 4 Categories and atoms are held seperately to stop me making mistakes 5 6 Operates in terms of D strings 7 */ 8 module util.statman; 9 import std.json; 10 import std.exception : enforce; 11 12 public 13 { 14 struct StatManager 15 { 16 HasCategory buf; 17 18 this(string nameset) 19 { 20 buf = new HasCategory; 21 buf.setAtom("from", nameset); 22 } 23 24 alias buf this; 25 StatManager* gcDup() 26 { 27 auto y = new StatManager; 28 y.buf = this.buf; 29 return y; 30 } 31 } 32 } 33 // Hidden implementation detailed 34 private: 35 36 ///The base of all stats 37 interface HasJSON 38 { 39 JSONValue getJSON() const pure; 40 string prettyString(uint depth = 0); 41 } 42 43 class Atom(T) : HasJSON 44 { 45 private T x; 46 this(T xx) 47 { 48 import std.traits : hasIndirections; 49 50 static if (hasIndirections!T) 51 { 52 x = xx.idup; 53 } 54 else 55 { 56 x = xx; 57 } 58 } 59 60 JSONValue getJSON() const pure 61 { 62 return JSONValue(x); 63 } 64 65 string prettyString(uint depth = 0) 66 { 67 import std.conv; 68 69 return to!string(x); 70 } 71 } 72 class Array : HasJSON { 73 string name; 74 this(string _name) 75 { 76 name = _name; 77 } 78 private HasCategory[] content; 79 HasCategory index(ulong i) 80 { 81 return content[i]; 82 } 83 HasCategory bump() 84 { 85 auto y = new HasCategory; 86 content ~= y; 87 return y; 88 } 89 void debg() const 90 { 91 import std.stdio; 92 writeln("Stored: ", content.length); 93 } 94 JSONValue getJSON() const pure 95 { 96 97 JSONValue g; 98 g.array = []; 99 foreach(i, v; content) 100 { 101 g.array ~= v.getJSON; 102 } 103 return g; 104 } 105 string prettyString(uint depth = 0) 106 { 107 import std.array : replicate; 108 import std.format; 109 alias tabrep = x => "\t".replicate(x); 110 const tabs = tabrep(depth); 111 string tmp = tabs; 112 foreach(i, j; content) 113 { 114 tmp ~= format!"Index: %d\n%s%s"(i, tabs, j.prettyString(depth + 1)); 115 } 116 return tmp; 117 } 118 } 119 public class HasCategory : HasJSON 120 { 121 HasJSON[string] atomMap; 122 123 void setAtom(T)(string name, T x) 124 { 125 atomMap[name] = new Atom!T(x); 126 } 127 Array initArray(string name) 128 { 129 if(name !in atomMap) { 130 auto y = new Array(name); 131 atomMap[name] = y; 132 return y; 133 } else { 134 return cast(Array) atomMap[name]; 135 } 136 137 138 } 139 HasCategory[string] categoryMap; 140 HasCategory category(string name) 141 { 142 enforce(name !in atomMap, "Name collision: category is also an atom"); 143 if (name in categoryMap) 144 { 145 return categoryMap[name]; 146 } 147 else 148 { 149 categoryMap[name] = new HasCategory; 150 return categoryMap[name]; 151 } 152 } 153 //Attempts to be slightly clever: Get the identifier from a symbol and use that to assign an atom 154 void setAtom(alias var)() 155 { 156 setAtom(__traits(identifier, var), var); 157 } 158 159 JSONValue getJSON() const pure 160 { 161 JSONValue buf; 162 foreach (key, value; atomMap) 163 { 164 buf[key] = value.getJSON; 165 } 166 foreach (key, value; categoryMap) 167 { 168 buf[key] = value.getJSON; 169 } 170 return buf; 171 } 172 173 private string[2][] getAtoms() 174 { 175 string[2][] tmp; 176 foreach (key, value; atomMap) 177 { 178 tmp ~= [key, value.prettyString]; 179 } 180 return tmp; 181 } 182 183 string prettyString(uint depth = 0) 184 { 185 //ugly 186 import std.stdio; 187 alias tabrep = x => "\t".replicate(x); 188 189 import util.colour; 190 import std.format; 191 import std.algorithm; 192 import std.array : replicate; 193 194 const tabs = "\t".replicate(depth); 195 string tmp; 196 197 198 199 foreach (key, value; atomMap) 200 { 201 202 tmp ~= format!"%s%s: %s\n"(tabs, colourString(key, ForegroundColour.Yellow), 203 colourString(value.prettyString(0), ForegroundColour.Red)); 204 } 205 206 207 foreach (key, value; categoryMap) 208 { 209 const colouredKey = key.colourString(ForegroundColour.Green); 210 tmp ~= format!"%s%s\n%s\n"(tabs, colouredKey, value.prettyString(depth + 1)); 211 212 } 213 214 return tmp; 215 } 216 }