1 module glued.annotations; 2 3 import std.conv; 4 import std.traits; 5 import std.meta; 6 7 //todo Ive prepared all that while Im not sure its useful... 8 //its useful in context definition, maybe move to dedicated module? 9 struct Target { 10 enum Type { 11 METHOD = 1<<0, 12 FIELD = 1<<1, 13 INTERFACE = 1<<2, 14 CLASS = 1<<3, 15 STRUCT = 1<<4, 16 ENUM = 1<<5, 17 //todo annotation? 18 19 MEMBER = (METHOD | FIELD), 20 21 DATA = (STRUCT | ENUM), 22 POINTER = (INTERFACE | CLASS), 23 TYPE = (DATA | POINTER), 24 25 ANY = (MEMBER | TYPE) 26 } 27 28 Type[] types; 29 int mask; 30 31 this(Type[] types...){ 32 assert(types.length); //todo 33 this.types = types; 34 foreach (Type type; types) 35 mask = mask | type; 36 } 37 38 /** 39 * @param checked - type of element that annotation (annotated with this Target) was put on 40 * @return - if the annotation annotated with Target can be put on checked type of element 41 */ 42 bool canAnnotate(Type checked){ 43 return (mask & to!int(checked)) > 0; 44 } 45 } 46 47 template TargetTypeOf(T...) if (T.length == 1) { 48 static if (is(T[0])) { 49 static if (is(T[0] == class)){ 50 enum TargetTypeOf = Target.Type.CLASS; 51 } 52 else 53 static if (is(T[0] == interface)){ 54 enum TargetTypeOf = Target.Type.INTERFACE; 55 } 56 else 57 static if (is(T[0] == struct)){ 58 enum TargetTypeOf = Target.Type.STRUCT; 59 } 60 static if (is(T[0] == enum)){ 61 enum TargetTypeOf = Target.Type.ENUM; 62 } 63 } else { 64 static assert(false, "support for methods and fields is coming"); 65 } 66 } 67 68 //todo disable constructors 69 struct Implies(S) if (is(S == struct)) { //todo if isAnnotation? 70 const S implicated = S.init; 71 72 template getImplicated(){ 73 alias getImplicated = Alias!(S.init); 74 } 75 } 76 77 struct Implies(alias S) if (is(typeof(S) == struct)) { //todo ditto 78 const typeof(S) implicated = S; 79 80 template getImplicated(){ 81 alias getImplicated = Alias!(S); 82 } 83 } 84 85 86 enum onlyStructs(alias X) = is(X) ? is(X == struct) : is (typeof(X) == struct); 87 88 enum expandToData(alias X) = is(X) ? X.init : X; 89 90 template getType(alias X) if (!is(X)) { 91 alias getType = typeof(X); 92 } 93 94 template getExplicitAnnotations(alias M) { 95 alias getExplicitAnnotations = staticMap!(expandToData, Filter!(onlyStructs, __traits(getAttributes, M))); 96 } 97 98 template getExplicitAnnotationTypes(alias M) { 99 alias getExplicitAnnotationTypes= staticMap!(getType, getExplicitAnnotations!M); 100 } 101 102 template getImplicitAnnotations(alias M) { 103 template toTypes(X...) { 104 alias toType(alias X) = typeof(X); 105 alias toTypes = staticMap!(toType, X); 106 } 107 108 template hackMe(alias X){ 109 alias hackMe = extractImplicit!X; 110 } 111 112 template extractImplicit(alias A) { 113 //correct way: check what not-Implies!(...) annotations imply, extract Implies from these 114 //add local Implies, profit 115 //todo 116 // static assert isAnnotation!A; 117 alias unpack(I) = I.getImplicated!(); 118 alias implications = getUDAs!(A, Implies); 119 alias locallyImplicated = staticMap!(unpack, implications); 120 static if (locallyImplicated.length > 0) 121 { 122 template step(int i, Acc...){ 123 static if (i == locallyImplicated.length) 124 alias step = Acc; 125 else { 126 alias step = step!(i+1, AliasSeq!(extractImplicit!(typeof(locallyImplicated[i])), Acc)); 127 } 128 } 129 alias theirImplications = step!(0);//staticMap!(extractImplicit, toTypes!theirImplications);//probably extract from all UDAs instead 130 alias extractImplicit = AliasSeq!(locallyImplicated, theirImplications); 131 } 132 else 133 { 134 alias extractImplicit = locallyImplicated; 135 } 136 } 137 138 alias getImplicitAnnotations = staticMap!(extractImplicit, toTypes!(getExplicitAnnotations!M)); 139 } 140 141 alias getAnnotations(alias M) = NoDuplicates!(AliasSeq!(getExplicitAnnotations!M, getImplicitAnnotations!M)); 142 143 template getAnnotations(alias M, T) { 144 import glued.utils: ofType; 145 alias getAnnotations = Filter!(ofType!T, getAnnotations!M); 146 } 147 148 template getAnnotation(alias M, T) { 149 import glued.utils: ofType; 150 //todo return None instead? allow ommiting by version? 151 static assert(hasOneAnnotation!(M, T)); 152 enum getAnnotation = getAnnotations!(M, T)[0]; 153 } 154 155 enum hasOneAnnotation(alias M, T) = getAnnotations!(M, T).length == 1; 156 157 enum hasAnnotation(alias M, T) = getAnnotations!(M, T).length > 0;