1 /// Automated accessor/property generation with support for flags.
2 module tern.accessors;
3 
4 /// Attribute signifying an enum uses flags.
5 public enum flags;
6 /// Attribute signifying an enum should not have properties made.
7 public enum exempt;
8 
9 /// Template mixin for auto-generating properties.  
10 /// Assumes standardized prefixes! (m_ for backing fields, k for masked enum values)  
11 /// Assumes standardized postfixes! (MASK or Mask for masks)  
12 // TODO: Overloads (allow devs to write specifically a get/set and have the counterpart auto generated)
13 //       ~Bitfield exemption?~
14 //       Conditional get/sets? (check flag -> return a default) (default attribute?)
15 //       Flag get/sets from pre-existing get/sets (see methodtable.d relatedTypeKind)
16 //       Auto import types (generics!!)
17 //       Allow for indiv. get/sets without needing both declared
18 /// Does not support multiple fields with the same enum type!
19 public template accessors()
20 {
21     import std.traits;
22     import std.string;
23     import std.meta;
24 
25     static foreach (string member; __traits(allMembers, typeof(this)))
26     {
27         static if (member.startsWith("m_") && !__traits(compiles, { enum _ = mixin(member); }) &&
28             isMutable!(TypeOf!(typeof(this), member)) &&
29             (isFunction!(__traits(getMember, typeof(this), member)) || staticIndexOf!(exempt, __traits(getAttributes, __traits(getMember, typeof(this), member))) == -1))
30         {
31             static if (!__traits(hasMember, typeof(this), member[2..$]))
32             {
33                 static if (!__traits(hasMember, typeof(this), member[2..$]))
34                 {
35                     mixin("final @property "~fullyQualifiedName!(TypeOf!(typeof(this), member))~" "~member[2..$]~"() { return "~member~"; }");
36                     mixin("final @property "~fullyQualifiedName!(TypeOf!(typeof(this), member))~" "~member[2..$]~"("~fullyQualifiedName!(TypeOf!(typeof(this), member))~" val) { "~member~" = val; return "~member~"; }");
37                 }
38 
39                 // Flags
40                 static if (is(TypeOf!(typeof(this), member) == enum) &&
41                     !seqContains!(exempt, __traits(getAttributes, TypeOf!(typeof(this), member))) &&
42                     seqContains!(flags, __traits(getAttributes, TypeOf!(typeof(this), member))))
43                 {
44                     static foreach (string flag; __traits(allMembers, TypeOf!(this, member)))
45                     {
46                         static if (flag.startsWith('k'))
47                         {
48                             static foreach_reverse (string mask; __traits(allMembers, TypeOf!(this, member))[0..staticIndexOf!(flag, __traits(allMembers, TypeOf!(this, member)))])
49                             {
50                                 static if (mask.endsWith("Mask") || mask.endsWith("MASK"))
51                                 {
52                                     static if (!__traits(hasMember, typeof(this), "is"~flag[1..$]))
53                                     {
54                                         // @property bool isEastern()...
55                                         mixin("final @property bool is"~flag[1..$]~"() { return ("~member[2..$]~" & "~fullyQualifiedName!(TypeOf!(this, member))~"."~mask~") == "~fullyQualifiedName!(TypeOf!(this, member))~"."~flag~"; }");
56                                         // @property bool isEastern(bool state)...
57                                         mixin("final @property bool is"~flag[1..$]~"(bool state) { return ("~member[2..$]~" = cast("~fullyQualifiedName!(TypeOf!(this, member))~")(state ? ("~member[2..$]~" & "~fullyQualifiedName!(TypeOf!(this, member))~"."~mask~") | "~fullyQualifiedName!(TypeOf!(this, member))~"."~flag~" : ("~member[2..$]~" & "~fullyQualifiedName!(TypeOf!(this, member))~"."~mask~") & ~"~fullyQualifiedName!(TypeOf!(this, member))~"."~flag~")) == "~fullyQualifiedName!(TypeOf!(this, member))~"."~flag~"; }");
58                                     }
59                                 }
60 
61                             }
62                         }
63                         else
64                         {  
65                             static if (!__traits(hasMember, typeof(this), "is"~flag))
66                             {
67                                 // @property bool isEastern()...
68                                 mixin("final @property bool is"~flag~"() { return ("~member[2..$]~" & "~fullyQualifiedName!(TypeOf!(this, member))~"."~flag~") != 0; }");
69                                 // @property bool isEastern(bool state)...
70                                 mixin("final @property bool is"~flag~"(bool state) { return ("~member[2..$]~" = cast("~fullyQualifiedName!(TypeOf!(this, member))~")(state ? ("~member[2..$]~" | "~fullyQualifiedName!(TypeOf!(this, member))~"."~flag~") : ("~member[2..$]~" & ~"~fullyQualifiedName!(TypeOf!(this, member))~"."~flag~"))) != 0; }");
71                             }
72                         }
73                     }
74                 }
75 
76                 // Non-flags
77                 static if (is(TypeOf!(typeof(this), member) == enum) &&
78                     !seqContains!(exempt, __traits(getAttributes, TypeOf!(typeof(this), member))) &&
79                     !seqContains!(flags, __traits(getAttributes, TypeOf!(typeof(this), member))))
80                 {
81                     static foreach (string flag; __traits(allMembers, TypeOf!(this, member)))
82                     {
83                         static if (!__traits(hasMember, typeof(this), "is"~flag))
84                         {
85                             // @property bool Eastern()...
86                             mixin("final @property bool is"~flag~"() { return "~member[2..$]~" == "~fullyQualifiedName!(TypeOf!(this, member))~"."~flag~"; }");
87                             // @property bool Eastern(bool state)...
88                             mixin("final @property bool is"~flag~"(bool state) { return ("~member[2..$]~" = "~fullyQualifiedName!(TypeOf!(this, member))~"."~flag~") == "~fullyQualifiedName!(TypeOf!(this, member))~"."~flag~"; }");
89                         }
90                     }
91                 }
92             }
93         }
94     }
95 }