1 /++
2     Type constructors.
3 
4     Copyright: [JR](https://github.com/zorael)
5     License: [Boost Software License 1.0](https://www.boost.org/users/license.html)
6 
7     Authors:
8         [JR](https://github.com/zorael)
9  +/
10 module lu.typecons;
11 
12 private:
13 
14 public:
15 
16 
17 // UnderscoreOpDispatcher
18 /++
19     Mixin template generating an `opDispatch` redirecting calls to members whose
20     names match the passed variable string but with an underscore prepended.
21  +/
22 mixin template UnderscoreOpDispatcher()
23 {
24     version(unittest)
25     {
26         import lu.traits : MixinConstraints, MixinScope;
27         mixin MixinConstraints!(
28             (MixinScope.struct_ | MixinScope.class_ | MixinScope.union_),
29             typeof(this).stringof);
30     }
31 
32     /++
33         Mutator.
34 
35         Params:
36             var = The variable name to set.
37             value = The value to set the variable to.
38 
39         Returns:
40             A reference to the object which this is mixed into.
41      +/
42     ref auto opDispatch(string var, T)(T value)
43     {
44         import std.traits : isArray, isAssociativeArray, isSomeString;
45 
46         static if (!var.length)
47         {
48             import std.format : format;
49             enum pattern = "Empty variable name passed to `%s.opDispatch`";
50             enum message = pattern.format(typeof(this).stringof);
51             static assert(0, message);
52         }
53 
54         enum realVar = '_' ~ var;
55         alias V = typeof(mixin(realVar));
56 
57         static if (isAssociativeArray!V)
58         {
59             import lu.meld : MeldingStrategy, meldInto;
60             value.meldInto!(MeldingStrategy.overwriting)(mixin(realVar));
61         }
62         else static if (isArray!V && !isSomeString!V)
63         {
64             mixin(realVar) ~= value;
65         }
66         else
67         {
68             mixin(realVar) = value;
69         }
70 
71         return this;
72     }
73 
74     /++
75         Accessor.
76 
77         Params:
78             var = The variable name to get.
79 
80         Returns:
81             The value of the variable.
82      +/
83     ref auto opDispatch(string var)() inout
84     {
85         static if (!var.length)
86         {
87             import std.format : format;
88             enum pattern = "Empty variable name passed to `%s.opDispatch`";
89             enum message = pattern.format(typeof(this).stringof);
90             static assert(0, message);
91         }
92 
93         enum realVar = '_' ~ var;
94         return mixin(realVar);
95     }
96 }
97 
98 ///
99 unittest
100 {
101     {
102         struct Foo
103         {
104             int _i;
105             string _s;
106             bool _b;
107             string[] _add;
108             alias wordList = _add;
109 
110             mixin UnderscoreOpDispatcher;
111         }
112 
113         Foo f;
114         f.i = 42;         // f.opDispatch!"i"(42);
115         f.s = "hello";    // f.opDispatch!"s"("hello");
116         f.b = true;       // f.opDispatch!"b"(true);
117         f.add("hello");   // f.opDispatch!"add"("hello");
118         f.add("world");   // f.opDispatch!"add"("world");
119 
120         assert(f.i == 42);
121         assert(f.s == "hello");
122         assert(f.b);
123         assert(f.wordList == [ "hello", "world" ]);
124 
125         // ref auto allows this
126         ++f.i;
127         assert(f.i == 43);
128 
129         /+
130             Returns `this` by reference, so we can chain calls.
131          +/
132         auto f2 = Foo()
133             .i(9001)
134             .s("world")
135             .b(false)
136             .add("hello")
137             .add("world");
138 
139         assert(f2.i == 9001);
140         assert(f2.s == "world");
141         assert(!f2.b);
142         assert(f2.wordList == [ "hello", "world" ]);
143     }
144     {
145         struct Bar
146         {
147             string[string] _aa;
148 
149             mixin UnderscoreOpDispatcher;
150         }
151 
152         Bar bar;
153         bar.aa = [ "hello" : "world" ];
154         bar.aa = [ "foo" : "bar" ];
155         assert(bar.aa == [ "hello" : "world", "foo" : "bar"]);
156     }
157 }