1 module unit_threaded.uda;
2 
3 import unit_threaded.meta;
4 import std.traits;
5 import std.meta;
6 
7 /**
8  * For the given module, return true if this module's member has
9  * the given UDA. UDAs can be types or values.
10  */
11 template HasAttribute(alias module_, string member, alias attribute) {
12     mixin(importMember!module_(member));
13 
14     static if(!__traits(compiles, __traits(getAttributes, mixin(member))))
15         enum HasAttribute = false;
16     else {
17         enum isAttribute(alias T) = is(TypeOf!T == attribute);
18         alias attrs = Filter!(isAttribute, __traits(getAttributes, mixin(member)));
19 
20         static assert(attrs.length == 0 || attrs.length == 1,
21                       text("Maximum number of attributes is 1 for ", attribute));
22 
23         static if(attrs.length == 0) {
24             enum HasAttribute = false;
25         } else {
26             enum HasAttribute = true;
27         }
28     }
29 }
30 
31 /**
32  * For the given module, return true if this module's member has
33  * the given UDA. UDAs can be types or values.
34  */
35 template GetAttributes(alias module_, string member, A) {
36     mixin(importMember!module_(member));
37     enum isAttribute(alias T) = is(TypeOf!T == A);
38     alias GetAttributes = Filter!(isAttribute, __traits(getAttributes, mixin(member)));
39 }
40 
41 
42 /**
43  * Utility to allow checking UDAs regardless of whether the template
44  * parameter is or has a type
45  */
46 private template TypeOf(alias T) {
47     static if(__traits(compiles, typeof(T))) {
48         alias TypeOf = typeof(T);
49     } else {
50         alias TypeOf = T;
51     }
52 }
53 
54 
55 
56 unittest {
57     import unit_threaded.attrs;
58     import unit_threaded.tests.module_with_attrs;
59     import std.traits: hasUDA;
60 
61     //check for value UDAs
62     static assert(HasAttribute!(unit_threaded.tests.module_with_attrs, "testAttrs", HiddenTest));
63     static assert(HasAttribute!(unit_threaded.tests.module_with_attrs, "testAttrs", ShouldFail));
64     static assert(!HasAttribute!(unit_threaded.tests.module_with_attrs, "testAttrs", Name));
65 
66     //check for non-value UDAs
67     static assert(HasAttribute!(unit_threaded.tests.module_with_attrs, "testAttrs", SingleThreaded));
68     static assert(!HasAttribute!(unit_threaded.tests.module_with_attrs, "testAttrs", DontTest));
69 
70     static assert(HasAttribute!(unit_threaded.tests.module_with_attrs, "testValues", ShouldFail));
71 }
72 
73 template isTypesAttr(alias T) {
74     import unit_threaded.attrs;
75     enum isTypesAttr = is(T) && is(T:Types!U, U...);
76 }
77 
78 
79 /*
80  @Types is different from the other UDAs since it's a templated struct
81  None of the templates above work so we special case it here
82 */
83 
84 /// If a test has the @Types UDA
85 enum HasTypes(alias T) = GetTypes!T.length > 0;
86 
87 /// Returns the types in the @Types UDA associated to a test
88 template GetTypes(alias T) {
89     static if(!__traits(compiles, __traits(getAttributes, T))) {
90         alias GetTypes = AliasSeq!();
91     } else {
92         alias types = Filter!(isTypesAttr, __traits(getAttributes, T));
93         static if(types.length > 0)
94             alias GetTypes = TemplateArgsOf!(types[0]);
95         else
96             alias GetTypes = AliasSeq!();
97     }
98 }
99 
100 
101 ///
102 unittest {
103     import unit_threaded.attrs;
104     @Types!(int, float) int i;
105     static assert(HasTypes!i);
106     static assert(is(GetTypes!i == AliasSeq!(int, float)));
107 
108 }
109 
110 // copy of recent hasUDA from Phobos here because old
111 // compilers will fail otherwise
112 
113 enum hasUtUDA(alias symbol, alias attribute) = getUtUDAs!(symbol, attribute).length > 0;
114 
115 template getUtUDAs(alias symbol, alias attribute)
116 {
117     import std.meta : Filter;
118 
119     template isDesiredUDA(alias toCheck)
120     {
121         static if (is(typeof(attribute)) && !__traits(isTemplate, attribute))
122         {
123             static if (__traits(compiles, toCheck == attribute))
124                 enum isDesiredUDA = toCheck == attribute;
125             else
126                 enum isDesiredUDA = false;
127         }
128         else static if (is(typeof(toCheck)))
129         {
130             static if (__traits(isTemplate, attribute))
131                 enum isDesiredUDA =  isInstanceOf!(attribute, typeof(toCheck));
132             else
133                 enum isDesiredUDA = is(typeof(toCheck) == attribute);
134         }
135         else static if (__traits(isTemplate, attribute))
136             enum isDesiredUDA = isInstanceOf!(attribute, toCheck);
137         else
138             enum isDesiredUDA = is(toCheck == attribute);
139     }
140 
141     alias getUtUDAs = Filter!(isDesiredUDA, __traits(getAttributes, symbol));
142 }
143 
144 unittest {
145     import unit_threaded.attrs;
146     import unit_threaded.tests.module_with_attrs;
147     import std.traits: hasUDA;
148 
149     static assert(hasUtUDA!(unit_threaded.tests.module_with_attrs.testOtherAttrs, ShouldFailWith));
150     static assert(hasUtUDA!(unit_threaded.tests.module_with_attrs.testOtherAttrs, ShouldFailWith!Exception));
151     static assert(!hasUtUDA!(unit_threaded.tests.module_with_attrs.testOtherAttrs, ShouldFailWith!Throwable));
152 }