1 module glued.annotations.core_impl; 2 3 import std.meta: AliasSeq, Filter, staticMap, NoDuplicates, allSatisfy; 4 import std.traits; 5 import std.algorithm; 6 7 import glued.annotations.core_annotations; 8 import glued.annotations.validation_impl; 9 import glued.utils: ofType, toType, toAnnotableType; 10 11 private struct ParameterPointer(alias Foo) 12 { 13 alias Target = Foo; 14 string paramName; 15 size_t paramIdx; 16 } 17 18 template parameter(alias Foo, size_t paramIdx) 19 if (isCallable!(Foo) && paramIdx < Parameters!Foo.length) 20 { 21 enum parameter = ParameterPointer!(Foo)(ParameterIdentifierTuple!(Foo)[paramIdx], paramIdx); 22 } 23 24 25 template parameter(alias Foo, string paramName) 26 // remember that parameter names are given as tuple - wrap it in [...] for canFind to work properly 27 if (isCallable!(Foo) && [ ParameterIdentifierTuple!(Foo) ].canFind(paramName)) 28 { 29 static foreach (i, n; ParameterIdentifierTuple!(Foo)) 30 { 31 static if (n == paramName) 32 { 33 enum parameter = ParameterPointer!(Foo)(n, i); 34 } 35 //else is not required - we checked that parameter name is present in 36 // template definition and language itself disallows name repetition, 37 // so we are sure that this 'if' will trigger exactly once 38 } 39 } 40 41 enum isStructInstance(alias X) = is (typeof(X) == struct); 42 43 template isNotMagic(X...) 44 if (X.length == 1) 45 { 46 enum nonMagicalType(X) = !hasUDA!(X, GluedMagic); 47 static if (is(X)) 48 { 49 enum isNotMagic = nonMagicalType!X; 50 } 51 else 52 { 53 enum isNotMagic = allSatisfy!(nonMagicalType, toAnnotableType!(X)); 54 } 55 } 56 57 template expandToData(alias X) 58 { 59 static if (__traits(isTemplate, X)) 60 { 61 static if (__traits(compiles, X!())) 62 { 63 alias templateInstance = X!(); 64 } 65 else 66 { 67 static if (__traits(compiles, X!(void))) 68 { 69 alias templateInstance = X!(void); 70 } 71 else 72 { 73 pragma(msg, "CANNOT EXPAND ", X, " WITH DEFAULTS, SKIPPING"); 74 alias templateInstance = void; 75 } 76 } 77 static if (is(templateInstance == void)) 78 { 79 enum expandToData; 80 } 81 else 82 { 83 enum expandToData = expandToData!(templateInstance); 84 } 85 } 86 else 87 { 88 static if (is(X)) 89 { 90 static if (is(X == struct)) 91 { 92 enum expandToData = X.init; 93 } 94 else 95 { 96 enum expandToData; 97 } 98 } 99 else 100 { 101 enum expandToData = X; 102 } 103 } 104 } 105 106 template getExplicitAnnotations(alias M) 107 { 108 enum isParamPointer(alias X) = __traits(hasMember, X, "Target") && 109 __traits(hasMember, X, "paramName") && 110 is(typeof(X.paramName) == string) && 111 __traits(hasMember, X, "paramIdx") && 112 is(typeof(X.paramIdx) == size_t); 113 static if (isParamPointer!M) 114 { 115 116 alias OnParameterUDAs = staticMap!(expandToData, getUDAs!(M.Target, OnParameter)); 117 118 enum pred(alias U) = U.describesParam!(M.paramName, M.paramIdx); 119 alias relevant = Filter!(pred, OnParameterUDAs); 120 121 enum unpack(alias X) = expandToData!(X.annotation); 122 alias getExplicitAnnotations = staticMap!(unpack, relevant); 123 } 124 else 125 { 126 enum pred(alias X) = isStructInstance!X && isNotMagic!X; 127 alias getExplicitAnnotations = Filter!(pred, staticMap!(expandToData, __traits(getAttributes, M))); 128 } 129 } 130 131 template getExplicitAnnotationTypes(alias M) 132 { 133 alias getRawType(alias T) = typeof(T); 134 alias getExplicitAnnotationTypes= NoDuplicates!(staticMap!(toType, getExplicitAnnotations!M), staticMap!(getRawType, getExplicitAnnotations!M)); 135 } 136 137 template toTypes(X...) 138 { 139 alias toTypes = staticMap!(toType, X); 140 } 141 142 template extractImplicit(alias A) 143 { 144 //there may be some weird bug here, I suppose it will come up at some point 145 //correct way: check what not-Implies!(...) annotations imply, extract Implies from these 146 //add local Implies, profit 147 alias unpack(I) = I.getImplicated!(); 148 alias implications = getUDAs!(A, Implies); 149 alias locallyImplicated = staticMap!(unpack, implications); 150 static if (locallyImplicated.length > 0) 151 { 152 template step(size_t i, Acc...) 153 { 154 static if (i == locallyImplicated.length) 155 { 156 alias step = Acc; 157 } 158 else 159 { 160 alias types = toAnnotableType!(locallyImplicated[i]); 161 template innerStep(size_t j, Acc2...) 162 { 163 static if (j == types.length) 164 { 165 alias innerStep = Acc2; 166 } 167 else 168 { 169 alias innerStep = innerStep!(j+1, AliasSeq!(extractImplicit!(types[j]), Acc2)); 170 } 171 } 172 alias step = step!(i+1, AliasSeq!(innerStep!0, Acc)); 173 } 174 } 175 //I've had at least 4 distinct approaches to proper staticMap-based 176 //solution to this 177 //if you gave up on life and just want to waste as much time as possible 178 //before sweet death takes you away, have a go 179 // 180 //PS. tell "resursive template expansion" that I've said 'hi, I still hate you' 181 alias theirImplications = step!(0); 182 alias extractImplicit = AliasSeq!(locallyImplicated, theirImplications); 183 } 184 else 185 { 186 alias extractImplicit = locallyImplicated; 187 } 188 } 189 190 template getImplicitAnnotations(alias M) 191 { 192 alias getImplicitAnnotations = staticMap!(extractImplicit, getExplicitAnnotationTypes!M); 193 } 194 195 alias getUncheckedAnnotations(alias M) = AliasSeq!(NoDuplicates!(AliasSeq!(getExplicitAnnotations!M, getImplicitAnnotations!M))); 196 197 alias getAnnotations(alias M) = AliasSeq!(staticMap!(performCheck!M.on, getUncheckedAnnotations!M)); 198 199 template getAnnotations(alias M, alias T) 200 { 201 alias pred = ofType!T; 202 alias getAnnotations = Filter!(pred, getAnnotations!M); 203 } 204 205 template getAnnotation(alias M, alias T) 206 { 207 static assert(hasOneAnnotation!(M, T)); 208 enum getAnnotation = getAnnotations!(M, T)[0]; 209 } 210 211 enum hasOneAnnotation(alias M, alias T) = getAnnotations!(M, T).length == 1; 212 213 enum hasAnnotation(alias M, alias T) = getAnnotations!(M, T).length > 0;