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;