1 
2 module witchcraft.mixins.fields;
3 
4 mixin template WitchcraftField(T)
5 {
6     import witchcraft;
7 
8     import std.meta;
9     import std.traits;
10     import std.variant;
11 
12     static class FieldMixin(alias T, string name) : Field
13     {
14     private:
15         alias member = Alias!(__traits(getMember, T, name));
16 
17         enum bool writable = __traits(compiles, {
18             T instance = void;
19             typeof(member) value = void;
20 
21             __traits(getMember, instance, name) = value;
22         });
23 
24     public:
25         override Variant get(Variant instance) const
26         {
27             static if(is(T == class))
28             {
29                 auto this_ = cast(ClassInfo) typeid(T);
30                 auto other = cast(ClassInfo) instance.type;
31 
32                 // Ensure both types exist and can be converted.
33                 if(!this_ || !other || !(_d_isbaseof(this_, other) || _d_isbaseof(other, this_)))
34                 {
35                     assert(0, "Instance isn't type of `" ~ T.stringof ~ "`.");
36                 }
37 
38                 auto obj = instance.coerce!T;
39             }
40             else static if(is(T))
41             {
42                 auto obj = instance.get!T;
43             }
44             else
45             {
46                 alias obj = T;
47             }
48 
49             return Variant(__traits(getMember, obj, name));
50         }
51 
52         const(Attribute)[] getAttributes() const
53         {
54             alias attributes = AliasSeq!(__traits(getAttributes, member));
55             auto values = new Attribute[attributes.length];
56 
57             foreach(index, attribute; attributes)
58             {
59                 values[index] = new AttributeImpl!attribute;
60             }
61 
62             return values;
63         }
64 
65         const(Type) getDeclaringType() const
66         {
67             alias Parent = Alias!(__traits(parent, member));
68 
69             return inspect!Parent;
70         }
71 
72         const(TypeInfo) getDeclaringTypeInfo() const
73         {
74             alias Parent = Alias!(__traits(parent, member));
75 
76             static if(__traits(compiles, typeid(Parent)))
77             {
78                 return typeid(Parent);
79             }
80             else
81             {
82                 return null;
83             }
84         }
85 
86         @property
87         string getName() const
88         {
89             return name;
90         }
91 
92         string getFullName() const
93         {
94             return fullyQualifiedName!member;
95         }
96 
97         string getProtection() const
98         {
99             return __traits(getProtection, __traits(getMember, T, name));
100         }
101 
102         override const(Type) getValueType() const
103         {
104             return inspect!(typeof(member));
105         }
106 
107         override const(TypeInfo) getValueTypeInfo() const
108         {
109             return typeid(typeof(member));
110         }
111 
112         @property
113         final bool isAccessible() const
114         {
115             return true;
116         }
117 
118         @property
119         override bool isStatic() const
120         {
121             return __traits(compiles, {
122                 auto value = __traits(getMember, T, name);
123             });
124         }
125 
126         @property
127         override bool isWritable() const
128         {
129             return writable;
130         }
131 
132         override void set(Variant instance, Variant value) const
133         {
134             static if(is(T == class))
135             {
136                 auto this_ = cast(ClassInfo) typeid(T);
137                 auto other = cast(ClassInfo) instance.type;
138 
139                 // Ensure both types exist and can be converted.
140                 if(!this_ || !other || !(_d_isbaseof(this_, other) || _d_isbaseof(other, this_)))
141                 {
142                     assert(0, "Instance isn't type of `" ~ T.stringof ~ "`.");
143                 }
144 
145                 auto obj = instance.coerce!T;
146             }
147             else static if(is(T))
148             {
149                 auto obj = instance.get!T;
150             }
151             else
152             {
153                 alias obj = T;
154             }
155 
156             static if(writable)
157             {
158                 __traits(getMember, obj, name) = value.get!(typeof(member));
159             }
160             else
161             {
162                 assert("Field " ~ name ~ " is not writable.");
163             }
164         }
165     }
166 }