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 : isSomeString; 44 import graphql : GQLDCustomLeaf; 45 import nullablestore : NullableStore; 46 import std.datetime : SysTime, Date, DateTime, TimeOfDay; 47 import core.time : Duration; 48 49 static if(is(T == Nullable!Fs, Fs...)) { 50 if(t.isNull()) { 51 o("null"); 52 } else { 53 printerImpl(o, t.get()); 54 } 55 } else static if(is(T : GQLDCustomLeaf!K, K...)) { 56 printerImpl(o, t.value); 57 } else static if(is(T : NullableStore!G, G)) { 58 o(T.stringof); 59 // NullableStore stores no data 60 } else static if(isSomeString!T) { 61 o("\""); 62 o(to!string(t)); 63 o("\""); 64 } else static if(is(T == DateTime) || is(T == TimeOfDay) || is(T == Date) 65 || is(T == SysTime)) 66 { 67 o(t.toISOExtString()); 68 } else static if(is(T == Duration)) { 69 o(t.toString()); 70 } else static if(is(T == struct) || is(T == class)) { 71 enum mems = AllFieldNames!T; 72 o(T.stringof); 73 o("("); 74 static if(mems.length > 0) { 75 o(mems[0]); 76 o(": "); 77 enum mOne = mems[0]; 78 printerImpl(o, __traits(getMember, t, mOne)); 79 static foreach(mem; mems[1 .. $]) { 80 o(", "); 81 o(mem); 82 o(": "); 83 printerImpl(o, __traits(getMember, t, mem)); 84 } 85 } 86 o(")"); 87 } else { 88 o(to!string(t)); 89 } 90 } 91 92 unittest { 93 import std.typecons : Nullable; 94 95 struct Foo { 96 int a; 97 string b; 98 bool c; 99 Nullable!string d; 100 } 101 102 auto f = Foo(13, "Hello World", true); 103 string s = format("%s", aggPrinter(f)); 104 assert(s == `Foo(a: 13, b: "Hello World", c: true, d: null)`, s); 105 } 106 107 unittest { 108 import std.typecons : Nullable; 109 import std.format; 110 111 class Bar { 112 int a; 113 string b; 114 bool c; 115 Nullable!string d; 116 117 this(int a, string b, bool c) { 118 this.a = a; 119 this.b = b; 120 this.c = c; 121 } 122 } 123 124 auto f = new Bar(13, "Hello World", true); 125 string s = format("%s", aggPrinter(f)); 126 assert(s == `Bar(a: 13, b: "Hello World", c: true, d: null)`, s); 127 } 128 129 unittest { 130 import std.typecons : Nullable; 131 import std.format; 132 133 class Cls { 134 int a; 135 string b; 136 bool c; 137 Nullable!string d; 138 139 this(int a, string b, bool c) { 140 this.a = a; 141 this.b = b; 142 this.c = c; 143 } 144 } 145 146 class SubClass : Cls { 147 long e; 148 this(int a, string b, bool c, long e) { 149 super(a, b, c); 150 this.e = e; 151 } 152 } 153 154 auto f = new SubClass(13, "Hello World", true, 1337); 155 string s = format("%s", aggPrinter(f)); 156 string exp = `SubClass(a: 13, b: "Hello World", c: true, d: null, e: 1337)`; 157 string as = format("\next: %s\ngot: %s", exp, s); 158 assert(s == exp, as); 159 } 160 161 unittest { 162 import std.typecons : Nullable; 163 164 struct Foo { 165 Nullable!int a; 166 } 167 168 Foo f; 169 f.a = 1337; 170 string s = format("%s", aggPrinter(f)); 171 assert(s == "Foo(a: 1337)", s); 172 } 173 unittest { 174 import std.typecons : Nullable; 175 import graphql.uda : GQLDCustomLeaf; 176 import nullablestore; 177 178 int toInt(string s) { 179 return to!int(s); 180 } 181 182 string fromInt(int i) { 183 return to!string(i); 184 } 185 186 alias QInt = GQLDCustomLeaf!(int, fromInt, toInt); 187 alias QNInt = GQLDCustomLeaf!(Nullable!int, fromInt, toInt); 188 189 struct Foo { 190 Nullable!int a; 191 QNInt b; 192 QInt c; 193 NullableStore!(int[]) d; 194 } 195 196 Foo f; 197 f.a = 1337; 198 string s = format("%s", aggPrinter(f)); 199 string exp = "Foo(a: 1337, b: null, c: 0, d: NullableStore!(int[]))"; 200 assert(s == exp, format("\next: %s\ngot: %s", exp, s)); 201 202 Foo f2; 203 f2.a = 1338; 204 f2.b.value = nullable(37); 205 s = format("%s", aggPrinter(f2)); 206 exp = "Foo(a: 1338, b: 37, c: 0, d: NullableStore!(int[]))"; 207 assert(s == exp, format("\next: %s\ngot: %s", exp, s)); 208 } 209 210 unittest { 211 import std.stdio; 212 import std.datetime; 213 static struct Foo { 214 int a; 215 float b; 216 DateTime dt; 217 Date d; 218 TimeOfDay tod; 219 } 220 221 static struct Bar { 222 Foo foo; 223 Nullable!Foo foo2; 224 string c; 225 Duration dur; 226 TickDuration tdur; 227 } 228 229 Bar b; 230 string s = format("%s", aggPrinter(b)); 231 string exp = 232 `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))` 233 ; 234 assert(s == exp, s); 235 }