1 /** 2 * Internal helper functions/templates. 3 * 4 * Authors: <a href="https://github.com/rcorre">rcorre</a> 5 * License: <a href="http://opensource.org/licenses/MIT">MIT</a> 6 * Copyright: Copyright © 2015, rcorre 7 * Date: 3/29/15 8 */ 9 module jsonizer.internal.util; 10 11 import std.typetuple; 12 import jsonizer.internal.attribute; 13 14 /// Return members of T that could be serializeable. 15 /// Use `jsonizeKey` to reduce the result of `filteredMembers` to only members marked for 16 /// serialization 17 template filteredMembers(T) { 18 enum filteredMembers = 19 EraseAll!("_toJSON", 20 EraseAll!("_fromJSON", 21 EraseAll!("__ctor", 22 __traits(allMembers, T)))); 23 } 24 25 /// if member is marked with @jsonize("someName"), returns "someName". 26 /// if member is marked with @jsonize, returns the name of the member. 27 /// if member is not marked with @jsonize, returns null 28 /// example usage within a struct/class: 29 /// enum key = jsonizeKey!(__traits(getMember, this, member), member) 30 template jsonizeKey(alias member, string defaultName) { 31 alias found = findAttribute!(jsonize, member); 32 33 static if (found.length == 0) { 34 // no @jsonize attribute found 35 enum jsonizeKey = null; 36 } 37 else static if (isValueAttribute!(found[$ - 1])) { 38 // most-nested attribute is @jsonize(args...), key is attr.key 39 enum key = found[$ - 1].key; 40 enum jsonizeKey = (key is null) ? defaultName : key; 41 } 42 else { 43 // most-nested attribute is @jsonize, no custom key specified 44 enum jsonizeKey = defaultName; 45 } 46 } 47 48 /// Hack to catch and ignore aliased types. 49 /// for example, a class contains 'alias Integer = int', 50 /// it will be redirected to this template and return null (cannot be jsonized) 51 template jsonizeKey(T, string unused) { 52 enum jsonizeKey = null; 53 } 54 55 /// return true if member is marked with @jsonize(JsonizeOptional.yes). 56 template isOptional(alias member) { 57 alias found = Filter!(isValueAttribute, findAttribute!(jsonize, member)); 58 59 // find an explicit JsonizeOptional parameter. 60 // start with the attribute closest to the member and move outwards. 61 template helper(attrs ...) { 62 static if (attrs.length == 0) { 63 // recursion endpoint, no JsonizeOptional found. 64 enum helper = false; 65 } 66 else static if (attrs[$ - 1].optional == JsonizeOptional.unspecified) { 67 // unspecified, recurse to less-nested attribute 68 enum helper = helper!(attrs[0 .. $ - 1]); 69 } 70 else { 71 // specified either yes or no, use that value 72 enum helper = (attrs[$ - 1].optional == JsonizeOptional.yes); 73 } 74 } 75 76 enum isOptional = helper!(found); 77 } 78 79 /// Get a tuple of all attributes on `sym` matching `attr`. 80 template findAttribute(alias attr, alias sym) { 81 static assert(__traits(compiles, __traits(getAttributes, sym)), 82 "cannot get attributes of " ~ sym.stringof); 83 84 template match(alias a) { 85 enum match = (is(a == attr) || is(typeof(a) == attr)); 86 } 87 88 alias findAttribute = Filter!(match, __traits(getAttributes, sym)); 89 } 90 91 unittest { 92 struct attr { int i; } 93 struct junk { int i; } 94 95 void fun0() { } 96 @attr void fun1() { } 97 @attr(2) void fun2() { } 98 @junk @attr void fun3() { } 99 @attr(3) @junk @attr void fun4() { } 100 101 static assert(findAttribute!(attr, fun0).length == 0); 102 static assert(findAttribute!(attr, fun1).length == 1); 103 static assert(findAttribute!(attr, fun2).length == 1); 104 static assert(findAttribute!(attr, fun3).length == 1); 105 static assert(findAttribute!(attr, fun4).length == 2); 106 107 struct S0 { } 108 @attr struct S1 { } 109 @junk @attr struct S { } 110 static assert(findAttribute!(attr, S).length == 1); 111 } 112 113 /// True if `sym` has an attribute `attr`. 114 template hasAttribute(alias attr, alias sym) { 115 enum hasAttribute = findAttribute!(attr, sym).length > 0; 116 } 117 118 /// `hasAttribute` 119 unittest { 120 enum attr; 121 122 void fun0() { } 123 @attr void fun1() { } 124 125 static assert(!hasAttribute!(attr, fun0)); 126 static assert( hasAttribute!(attr, fun1)); 127 } 128 129 /// True if `attr` has a value (e.g. it is not a type). 130 template isValueAttribute(alias attr) { 131 enum isValueAttribute = is(typeof(attr)); 132 } 133 134 /// `isValueAttribute` 135 unittest { 136 struct attr { int i; } 137 138 @attr @attr(3) void fun() { } 139 140 static assert(!isValueAttribute!(__traits(getAttributes, fun)[0])); 141 static assert( isValueAttribute!(__traits(getAttributes, fun)[1])); 142 } 143 144 auto getMember(string name)() { 145 return (x) => __traits(getMember, x, name); 146 } 147 148 T construct(T, P, Params ...)(P parent, Params params) { 149 static if (!is(P == typeof(null))) { 150 return parent..new T(params); 151 } 152 else static if (is(typeof(T(params)) == T)) { 153 return T(params); 154 } 155 else static if (is(typeof(new T(params)) == T)) { 156 return new T(params); 157 } 158 else { 159 static assert(0, "Cannot construct"); 160 } 161 } 162 163 unittest { 164 static struct Foo { 165 this(int i) { this.i = i; } 166 int i; 167 } 168 169 assert(construct!Foo(null).i == 0); 170 assert(construct!Foo(null, 4).i == 4); 171 assert(!__traits(compiles, construct!Foo("asd"))); 172 } 173 174 unittest { 175 static class Foo { 176 this(int i) { this.i = i; } 177 178 this(int i, string s) { 179 this.i = i; 180 this.s = s; 181 } 182 183 int i; 184 string s; 185 } 186 187 assert(construct!Foo(null, 4).i == 4); 188 assert(construct!Foo(null, 4, "asdf").s == "asdf"); 189 assert(!__traits(compiles, construct!Foo("asd"))); 190 } 191 192 unittest { 193 class Foo { 194 class Bar { 195 int i; 196 this(int i) { this.i = i; } 197 } 198 } 199 200 auto f = new Foo; 201 assert(construct!(Foo.Bar)(f, 2).i == 2); 202 } 203 204 template hasDefaultCtor(T) { 205 import std.traits : isNested; 206 static if (isNested!T) { 207 alias P = typeof(__traits(parent, T).init); 208 enum hasDefaultCtor = is(typeof(P.init..new T()) == T); 209 } 210 else { 211 enum hasDefaultCtor = is(typeof(T()) == T) || is(typeof(new T()) == T); 212 } 213 } 214 215 version(unittest) { 216 struct S1 { } 217 struct S2 { this(int i) { } } 218 struct S3 { @disable this(); } 219 220 class C1 { } 221 class C2 { this(string s) { } } 222 class C3 { class Inner { } } 223 class C4 { class Inner { this(int i); } } 224 } 225 226 unittest { 227 static assert( hasDefaultCtor!S1); 228 static assert( hasDefaultCtor!S2); 229 static assert(!hasDefaultCtor!S3); 230 231 static assert( hasDefaultCtor!C1); 232 static assert(!hasDefaultCtor!C2); 233 234 static assert( hasDefaultCtor!C3); 235 static assert( hasDefaultCtor!(C3.Inner)); 236 static assert(!hasDefaultCtor!(C4.Inner)); 237 } 238 239 bool hasCustomJsonCtor(T)() { 240 static if (__traits(hasMember, T, "__ctor")) { 241 alias Overloads = TypeTuple!(__traits(getOverloads, T, "__ctor")); 242 foreach(overload ; Overloads) { 243 static if (staticIndexOf!(jsonize, __traits(getAttributes, overload)) >= 0) { 244 return true; 245 } 246 } 247 } 248 return false; 249 } 250 251 unittest { 252 static struct S1 { } 253 static struct S2 { this(int i); } 254 static struct S3 { @jsonize this(int i); } 255 static struct S4 { this(float f); @jsonize this(int i); } 256 257 static assert(!hasCustomJsonCtor!S1); 258 static assert(!hasCustomJsonCtor!S2); 259 static assert( hasCustomJsonCtor!S3); 260 static assert( hasCustomJsonCtor!S4); 261 262 static class C1 { } 263 static class C2 { this() {} } 264 static class C3 { @jsonize this() {} } 265 static class C4 { @jsonize this(int i); this(float f); } 266 267 static assert(!hasCustomJsonCtor!C1); 268 static assert(!hasCustomJsonCtor!C2); 269 static assert( hasCustomJsonCtor!C3); 270 static assert( hasCustomJsonCtor!C4); 271 }