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