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