1 module graphql.traits; 2 3 import std.meta; 4 import std.range : ElementEncodingType; 5 import std.traits; 6 import std.typecons : Nullable; 7 import std.experimental.logger : logf; 8 9 template AllIncarnations(T, SCH...) { 10 static if(SCH.length > 0 && is(T : SCH[0])) { 11 alias AllIncarnations = AliasSeq!(SCH[0], 12 .AllIncarnations!(T, SCH[1 .. $]) 13 ); 14 } else static if(SCH.length > 0) { 15 alias AllIncarnations = AliasSeq!(.AllIncarnations!(T, SCH[1 .. $])); 16 } else { 17 alias AllIncarnations = AliasSeq!(T); 18 } 19 } 20 21 template InheritedClasses(T) { 22 import std.meta : NoDuplicates, EraseAll; 23 alias Clss = InheritedClassImpl!T; 24 alias ND = NoDuplicates!(Clss); 25 alias NO = EraseAll!(Object, ND); 26 alias NOT = EraseAll!(T, NO); 27 alias InheritedClasses = NOT; 28 } 29 30 template InheritedClassImpl(T) { 31 import std.meta : staticMap, AliasSeq, NoDuplicates; 32 static if(is(T == union)) { 33 alias fields = staticMap!(.InheritedClassImpl, FieldTypeTuple!T); 34 alias tmp = AliasSeq!(T, fields); 35 alias InheritedClassImpl = tmp; 36 } else static if(is(T == class)) { 37 alias clss = staticMap!(.InheritedClassImpl, BaseClassesTuple!T); 38 alias interfs = staticMap!(.InheritedClassImpl, InterfacesTuple!T); 39 alias tmp = AliasSeq!(T, clss, interfs); 40 alias InheritedClassImpl = tmp; 41 } else static if(is(T == interface)) { 42 alias interfs = staticMap!(.InheritedClassImpl, InterfacesTuple!T); 43 alias tmp = AliasSeq!(T, interfs); 44 alias InheritedClassImpl = tmp; 45 } else static if(is(T : Nullable!F, F)) { 46 alias interfs = staticMap!(.InheritedClassImpl, F); 47 alias tmp = AliasSeq!(T, interfs); 48 alias InheritedClassImpl = tmp; 49 } else { 50 alias InheritedClassImpl = T; 51 } 52 } 53 54 unittest { 55 alias Bases = InheritedClasses!Union; 56 static assert(is(Bases == 57 AliasSeq!(Nullable!Bar, Bar, Nullable!Impl, Impl, Base)) 58 ); 59 } 60 61 unittest { 62 interface H { 63 int h(); 64 } 65 66 interface G : H { 67 float g(); 68 } 69 70 abstract class I : G { 71 } 72 73 abstract class J : I { 74 } 75 76 alias inter = InheritedClasses!G; 77 static assert(is(inter == AliasSeq!(H))); 78 79 alias inter2 = InheritedClasses!J; 80 static assert(is(inter2 == AliasSeq!(I,G,H))); 81 } 82 83 version(unittest) { 84 private: 85 abstract class Base { 86 int a; 87 } 88 89 class Impl : Base { 90 float b; 91 } 92 93 class Bar { 94 string c; 95 } 96 97 union Union { 98 Nullable!Bar foo; 99 Nullable!Impl impl; 100 } 101 } 102 103 template isNotObject(Type) { 104 enum isNotObject = !is(Type == Object); 105 } 106 107 template collectTypesImpl(Type) { 108 static if(is(Type == interface)) { 109 alias RetTypes = AliasSeq!(collectReturnType!(Type, 110 __traits(allMembers, Type)) 111 ); 112 alias ArgTypes = AliasSeq!(collectParameterTypes!(Type, 113 __traits(allMembers, Type)) 114 ); 115 alias collectTypesImpl = AliasSeq!(Type, RetTypes, ArgTypes, 116 InterfacesTuple!Type 117 ); 118 } else static if(is(Type == class)) { 119 alias RetTypes = AliasSeq!(collectReturnType!(Type, 120 __traits(allMembers, Type)) 121 ); 122 alias ArgTypes = AliasSeq!(collectParameterTypes!(Type, 123 __traits(allMembers, Type)) 124 ); 125 alias tmp = AliasSeq!( 126 Fields!(Type), 127 InheritedClasses!Type, 128 InterfacesTuple!Type 129 ); 130 alias collectTypesImpl = AliasSeq!(Type, tmp, RetTypes, ArgTypes); 131 } else static if(is(Type == union)) { 132 alias collectTypesImpl = AliasSeq!(Type, InheritedClasses!Type); 133 } else static if(is(Type : Nullable!F, F)) { 134 alias collectTypesImpl = .collectTypesImpl!(F); 135 } else static if(is(Type == struct)) { 136 alias RetTypes = AliasSeq!(collectReturnType!(Type, 137 __traits(allMembers, Type)) 138 ); 139 alias ArgTypes = AliasSeq!(collectParameterTypes!(Type, 140 __traits(allMembers, Type)) 141 ); 142 alias collectTypesImpl = AliasSeq!(Type, RetTypes, ArgTypes); 143 } else static if(isSomeString!Type) { 144 alias collectTypesImpl = string; 145 } else static if(is(Type == bool)) { 146 alias collectTypesImpl = bool; 147 } else static if(is(Type == enum)) { 148 alias collectTypesImpl = Type; 149 } else static if(isArray!Type) { 150 alias collectTypesImpl = .collectTypesImpl!(ElementEncodingType!Type); 151 } else static if(isIntegral!Type) { 152 alias collectTypesImpl = long; 153 } else static if(isFloatingPoint!Type) { 154 alias collectTypesImpl = float; 155 } else { 156 alias collectTypesImpl = AliasSeq!(); 157 } 158 } 159 160 template collectReturnType(Type, Names...) { 161 static if(Names.length > 0) { 162 static if(isCallable!(__traits(getMember, Type, Names[0]))) { 163 alias collectReturnType = AliasSeq!( 164 ReturnType!(__traits(getMember, Type, Names[0])), 165 .collectReturnType!(Type, Names[1 .. $]) 166 ); 167 } else { 168 alias collectReturnType = .collectReturnType!(Type, Names[1 .. $]); 169 } 170 } else { 171 alias collectReturnType = AliasSeq!(); 172 } 173 } 174 175 template collectParameterTypes(Type, Names...) { 176 static if(Names.length > 0) { 177 static if(isCallable!(__traits(getMember, Type, Names[0]))) { 178 alias ArgTypes = ParameterTypeTuple!( 179 __traits(getMember, Type, Names[0]) 180 ); 181 alias collectParameterTypes = AliasSeq!(ArgTypes, 182 .collectParameterTypes!(Type, Names[1 .. $]) 183 ); 184 } else { 185 alias collectParameterTypes = .collectParameterTypes!(Type, 186 Names[1 .. $] 187 ); 188 } 189 } else { 190 alias collectParameterTypes = AliasSeq!(); 191 } 192 } 193 194 template fixupBasicTypes(T) { 195 static if(isSomeString!T) { 196 alias fixupBasicTypes = string; 197 } else static if(is(T == enum)) { 198 alias fixupBasicTypes = T; 199 } else static if(is(T == bool)) { 200 alias fixupBasicTypes = bool; 201 } else static if(isIntegral!T) { 202 alias fixupBasicTypes = long; 203 } else static if(isFloatingPoint!T) { 204 alias fixupBasicTypes = float; 205 } else static if(isArray!T) { 206 alias ElemFix = fixupBasicTypes!(ElementEncodingType!T); 207 alias fixupBasicTypes = ElemFix[]; 208 } else static if(is(T : Nullable!F, F)) { 209 alias ElemFix = fixupBasicTypes!(F); 210 alias fixupBasicTypes = Nullable!(ElemFix); 211 } else { 212 alias fixupBasicTypes = T; 213 } 214 } 215 216 template noArrayOrNullable(T) { 217 static if(is(T : Nullable!F, F)) { 218 enum noArrayOrNullable = false; 219 } else static if(!isSomeString!T && isArray!T) { 220 enum noArrayOrNullable = false; 221 } else { 222 enum noArrayOrNullable = true; 223 } 224 } 225 226 unittest { 227 static assert(is(Nullable!int : Nullable!F, F)); 228 static assert(!is(int : Nullable!F, F)); 229 static assert( noArrayOrNullable!(int)); 230 static assert( noArrayOrNullable!(string)); 231 static assert(!noArrayOrNullable!(int[])); 232 static assert(!noArrayOrNullable!(Nullable!int)); 233 static assert(!noArrayOrNullable!(Nullable!int)); 234 } 235 236 template collectTypes(T...) { 237 alias oneLevelDown = NoDuplicates!(staticMap!(collectTypesImpl, T)); 238 alias basicT = staticMap!(fixupBasicTypes, oneLevelDown); 239 alias elemTypes = Filter!(noArrayOrNullable, basicT); 240 alias noVoid = EraseAll!(void, elemTypes); 241 alias rslt = NoDuplicates!(EraseAll!(Object, basicT), 242 EraseAll!(Object, noVoid) 243 ); 244 static if(rslt.length == T.length) { 245 alias collectTypes = rslt; 246 } else { 247 alias collectTypes = .collectTypes!(rslt); 248 } 249 } 250 251 version(unittest) { 252 package { 253 enum Enum { 254 one, 255 two 256 } 257 class U { 258 string f; 259 Baz baz; 260 Enum e; 261 } 262 class W { 263 Nullable!(Nullable!(U)[]) us; 264 } 265 class Y { 266 bool b; 267 Nullable!W w; 268 } 269 class Z : Y { 270 long id; 271 } 272 class Baz { 273 string id; 274 Z[] zs; 275 } 276 class Args { 277 float value; 278 } 279 interface Foo { 280 Baz bar(); 281 Args args(); 282 } 283 } 284 } 285 286 unittest { 287 alias a = collectTypes!(Enum); 288 static assert(is(a == AliasSeq!(Enum))); 289 } 290 291 unittest { 292 alias ts = collectTypes!(Foo); 293 alias expectedTypes = AliasSeq!(Foo, Baz, Args, float, Z[], Z, string, 294 long, Y, bool, Nullable!W, W, Nullable!(Nullable!(U)[]), U, Enum); 295 296 template canBeFound(T) { 297 enum tmp = staticIndexOf!(T, expectedTypes) != -1; 298 enum canBeFound = tmp; 299 } 300 static assert(allSatisfy!(canBeFound, ts)); 301 } 302 303 template stripArrayAndNullable(T) { 304 static if(is(T : Nullable!F, F)) { 305 alias stripArrayAndNullable = .stripArrayAndNullable!F; 306 } else static if(!isSomeString!T && isArray!T) { 307 alias stripArrayAndNullable = 308 .stripArrayAndNullable!(ElementEncodingType!T); 309 } else { 310 alias stripArrayAndNullable = T; 311 } 312 } 313 314 template stringofType(T) { 315 enum stringofType = T.stringof; 316 } 317 318 string[] interfacesForType(Schema)(string typename) { 319 import std.algorithm.searching : canFind; 320 alias filtered = staticMap!(stripArrayAndNullable, collectTypes!Schema); 321 alias Types = NoDuplicates!(filtered); 322 switch(typename) { 323 static foreach(T; Types) { 324 case T.stringof: { 325 static enum ret = [NoDuplicates!(staticMap!(stringofType, 326 EraseAll!(Object, AllIncarnations!(T, Types)))) 327 ]; 328 //logf("%s %s %s", typename, T.stringof, ret); 329 return ret; 330 } 331 } 332 default: 333 //logf("DEFAULT: '%s'", typename); 334 if(canFind(["__Type", "__Field", "__InputValue", "__Schema", 335 "__EnumValue", "__TypeKind", "__Directive", 336 "__DirectiveLocation"], typename)) 337 { 338 return [typename]; 339 } 340 return string[].init; 341 } 342 } 343 344 template PossibleTypes(Type, Schema) { 345 static if(is(Type == union)) { 346 alias PossibleTypes = Filter!(isAggregateType, FieldTypeTuple!Type); 347 } else static if(is(Type == interface) || is(Type == class)) { 348 alias AllTypes = NoDuplicates!(collectTypes!Schema); 349 alias PossibleTypes = NoDuplicates!(PossibleTypesImpl!(Type, AllTypes)); 350 } 351 } 352 353 template PossibleTypesImpl(Type, AllTypes...) { 354 static if(AllTypes.length == 0) { 355 alias PossibleTypesImpl = AliasSeq!(Type); 356 } else { 357 static if(is(AllTypes[0] : Type)) { 358 alias PossibleTypesImpl = AliasSeq!(AllTypes[0], 359 .PossibleTypesImpl!(Type, AllTypes[1 .. $]) 360 ); 361 } else { 362 alias PossibleTypesImpl = AliasSeq!( 363 .PossibleTypesImpl!(Type, AllTypes[1 .. $]) 364 ); 365 } 366 } 367 }