1 module aggregateprinter; 2 3 import std.conv : to; 4 import std.traits : FieldNameTuple, BaseClassesTuple; 5 import std.typecons : Nullable, nullable; 6 import std.meta : staticMap; 7 import std.format; 8 9 AggregatePrinter!T aggPrinter(T)(auto ref T t) { 10 return AggregatePrinter!T(&t); 11 } 12 13 struct AggregatePrinter(T) { 14 T* thing; 15 16 this(T* thing) { 17 this.thing = thing; 18 } 19 20 void toString(void delegate(const(char)[]) @safe output ) { 21 printerImpl(output, *(this.thing)); 22 } 23 } 24 25 private template ClassFieldsImpl(T) { 26 enum ClassFieldsImpl = FieldNameTuple!(T); 27 } 28 29 private template ClassFields(T) { 30 enum ClassFields = [staticMap!(ClassFieldsImpl, BaseClassesTuple!T)] 31 ~ [ClassFieldsImpl!T]; 32 } 33 34 private template AllFieldNames(T) { 35 static if(is(T == class)) { 36 enum AllFieldNames = ClassFields!T; 37 } else { 38 enum AllFieldNames = FieldNameTuple!T; 39 } 40 } 41 42 private void printerImpl(Out,T)(ref Out o, T t) { 43 import std.traits : isArray, isSomeString, Unqual; 44 import graphql : GQLDCustomLeaf; 45 import nullablestore : NullableStore; 46 import std.datetime : SysTime, Date, DateTime, TimeOfDay; 47 import core.time : Duration; 48 49 alias UT = Unqual!T; 50 51 static if(is(UT == Nullable!Fs, Fs...)) { 52 if(t.isNull()) { 53 o("null"); 54 } else { 55 printerImpl(o, t.get()); 56 } 57 } else static if(is(UT : GQLDCustomLeaf!K, K...)) { 58 printerImpl(o, t.value); 59 } else static if(is(UT : NullableStore!G, G)) { 60 o(T.stringof); 61 // NullableStore stores no data 62 } else static if(isSomeString!T) { 63 o("\""); 64 o(to!string(t)); 65 o("\""); 66 } else static if(isArray!T) { 67 o("["); 68 foreach(i; 0 .. t.length) { 69 o(i > 0 ? ", " : " "); 70 printerImpl(o, t[i]); 71 } 72 o("]"); 73 74 } else static if(is(UT : DateTime) || is(UT : TimeOfDay) || is(UT : Date) 75 || is(UT : SysTime)) 76 { 77 o(t.toISOExtString()); 78 } else static if(is(UT == Duration)) { 79 o(t.toString()); 80 } else static if(is(T == struct) || is(T == class)) { 81 enum mems = AllFieldNames!T; 82 o(T.stringof); 83 o("("); 84 static if(mems.length > 0) { 85 o(mems[0]); 86 o(": "); 87 enum mOne = mems[0]; 88 printerImpl(o, __traits(getMember, t, mOne)); 89 static foreach(mem; mems[1 .. $]) { 90 o(", "); 91 o(mem); 92 o(": "); 93 printerImpl(o, __traits(getMember, t, mem)); 94 } 95 } 96 o(")"); 97 } else { 98 o(to!string(t)); 99 } 100 } 101 102 unittest { 103 import std.typecons : Nullable; 104 105 struct Foo { 106 int a; 107 string b; 108 bool c; 109 Nullable!string d; 110 } 111 112 auto f = Foo(13, "Hello World", true); 113 string s = format("%s", aggPrinter(f)); 114 assert(s == `Foo(a: 13, b: "Hello World", c: true, d: null)`, s); 115 } 116 117 unittest { 118 import std.typecons : Nullable; 119 import std.format; 120 121 class Bar { 122 int a; 123 string b; 124 bool c; 125 Nullable!string d; 126 127 this(int a, string b, bool c) { 128 this.a = a; 129 this.b = b; 130 this.c = c; 131 } 132 } 133 134 auto f = new Bar(13, "Hello World", true); 135 string s = format("%s", aggPrinter(f)); 136 assert(s == `Bar(a: 13, b: "Hello World", c: true, d: null)`, s); 137 } 138 139 unittest { 140 import std.typecons : Nullable; 141 import std.format; 142 143 class Cls { 144 int a; 145 string b; 146 bool c; 147 Nullable!string d; 148 149 this(int a, string b, bool c) { 150 this.a = a; 151 this.b = b; 152 this.c = c; 153 } 154 } 155 156 class SubClass : Cls { 157 long e; 158 this(int a, string b, bool c, long e) { 159 super(a, b, c); 160 this.e = e; 161 } 162 } 163 164 auto f = new SubClass(13, "Hello World", true, 1337); 165 string s = format("%s", aggPrinter(f)); 166 string exp = `SubClass(a: 13, b: "Hello World", c: true, d: null, e: 1337)`; 167 string as = format("\next: %s\ngot: %s", exp, s); 168 assert(s == exp, as); 169 } 170 171 unittest { 172 import std.typecons : Nullable; 173 174 struct Foo { 175 Nullable!int a; 176 } 177 178 Foo f; 179 f.a = 1337; 180 string s = format("%s", aggPrinter(f)); 181 assert(s == "Foo(a: 1337)", s); 182 } 183 unittest { 184 import std.typecons : Nullable; 185 import graphql.uda : GQLDCustomLeaf; 186 import nullablestore; 187 188 int toInt(string s) { 189 return to!int(s); 190 } 191 192 string fromInt(int i) { 193 return to!string(i); 194 } 195 196 alias QInt = GQLDCustomLeaf!(int, fromInt, toInt); 197 alias QNInt = GQLDCustomLeaf!(Nullable!int, fromInt, toInt); 198 199 struct Foo { 200 Nullable!int a; 201 QNInt b; 202 QInt c; 203 NullableStore!(int[]) d; 204 } 205 206 Foo f; 207 f.a = 1337; 208 string s = format("%s", aggPrinter(f)); 209 string exp = "Foo(a: 1337, b: null, c: 0, d: NullableStore!(int[]))"; 210 assert(s == exp, format("\next: %s\ngot: %s", exp, s)); 211 212 Foo f2; 213 f2.a = 1338; 214 f2.b.value = nullable(37); 215 s = format("%s", aggPrinter(f2)); 216 exp = "Foo(a: 1338, b: 37, c: 0, d: NullableStore!(int[]))"; 217 assert(s == exp, format("\next: %s\ngot: %s", exp, s)); 218 } 219 220 unittest { 221 import std.stdio; 222 import std.datetime; 223 static struct Foo { 224 int a; 225 float b; 226 DateTime dt; 227 Date d; 228 TimeOfDay tod; 229 } 230 231 static struct Bar { 232 Foo foo; 233 Nullable!Foo foo2; 234 string c; 235 Duration dur; 236 TickDuration tdur; 237 Foo[] foos; 238 } 239 240 Bar b; 241 string s = format("%s", aggPrinter(b)); 242 string exp = 243 `Bar(foo: Foo(a: 0, b: nan, dt: 0001-01-01T00:00:00, d: 0001-01-01, tod: 00:00:00), foo2: null, c: "", dur: 0 hnsecs, tdur: TickDuration(length: 0), foos: [])` 244 ; 245 assert(s == exp, s); 246 247 const(Bar) c; 248 s = format("%s", aggPrinter(c)); 249 exp = 250 `const(Bar)(foo: const(Foo)(a: 0, b: nan, dt: 0001-01-01T00:00:00, d: 0001-01-01, tod: 00:00:00), foo2: null, c: "", dur: 0 hnsecs, tdur: const(TickDuration)(length: 0), foos: [])` 251 ; 252 assert(s == exp, s); 253 }