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;