1 module nxt.roptional;
2 
3 import nxt.nullable_traits : isNullable;
4 
5 private struct None {}
6 
7 struct Optional(T)
8 {
9     import core.internal.traits : Unqual;
10     private Unqual!T value;
11 
12     static if (!isNullable!T)
13     {
14         private bool present;
15     }
16 
17     this(T value)
18     {
19         opAssign(value);
20     }
21 
22     this(None)
23     {
24     }
25 
26     void opAssign(T value)
27     {
28         this.value = value;
29         static if (!isNullable!T)
30         {
31             present = true;
32         }
33     }
34 
35     void opAssign(None)
36     {
37         static if (isNullable!T)
38         {
39             value = null;
40         }
41         else
42         {
43             present = false;
44         }
45     }
46 
47     bool isPresent() const
48     {
49         static if (isNullable!T)
50         {
51             return value !is null;
52         }
53         else
54         {
55             return present;
56         }
57     }
58 
59     T get()
60     {
61         assert(isPresent);
62         return value;
63     }
64 
65     T or(lazy T alternativeValue)
66     {
67         return isPresent ? value : alternativeValue;
68     }
69 
70     bool empty() const
71     {
72         return !isPresent;
73     }
74 
75     T front()
76     {
77         return get;
78     }
79 
80     void popFront()
81     {
82         static if (isNullable!T)
83         {
84             value = null;
85         }
86         else
87         {
88             present = false;
89         }
90     }
91 
92     size_t length() const
93     {
94         return isPresent ? 1 : 0;
95     }
96 
97     // auto ref opDispatch(string name, Args...)(auto ref Args args)
98     // {
99     //     import std.traits : PointerTarget, isPointer;
100     //     import dlp.core.traits : hasField, TypeOfMember, getMember;
101 
102     //     static if (isPointer!T)
103     //         alias StoredType = PointerTarget!T;
104     //     else
105     //         alias StoredType = T;
106 
107     //     static if (is(StoredType == class) || is(StoredType == struct))
108     //     {
109     //         static if (hasField!(StoredType, name))
110     //         {
111     //             alias FieldType = TypeOfMember!(StoredType, name);
112 
113     //             if (isPresent)
114     //                 return optional(value.getMember!name);
115     //             else
116     //                 return none!FieldType;
117     //         }
118     //         else
119     //         {
120     //             alias ReturnType = typeof(__traits(getMember, value, name)(args));
121 
122     //             if (isPresent)
123     //                 return optional(__traits(getMember, value, name)(args));
124     //             else
125     //                 return none!ReturnType;
126     //         }
127     //     }
128     //     else
129     //     {
130     //         return optional(value.getMember!name);
131     //     }
132 
133     //     assert(0);
134     // }
135 
136     // @safe pure nothrow @nogc unittest
137     // {
138     //     assert(Optional!Foo(Foo(3)).a.get == 3);
139     //     assert(Optional!Foo.init.a.empty);
140 
141     //     assert(Optional!Foo(Foo()).opDispatch!"c"(4).get == 4);
142     //     assert(Optional!Foo.init.c(4).empty);
143 
144     //     assert(Optional!Foo(Foo(1, new Bar(5))).b.a.get == 5);
145     //     assert(Optional!Foo(Foo(1)).b.a.empty);
146     // }
147 }
148 
149 @safe pure nothrow @nogc unittest
150 {
151     enum newVale = 4;
152     Optional!int a = 3;
153     a = newVale;
154     assert(a.get == newVale);
155 }
156 
157 @safe pure nothrow @nogc unittest
158 {
159     Optional!int a = 3;
160     a = none;
161     assert(!a.isPresent);
162 }
163 
164 @safe pure nothrow @nogc unittest
165 {
166     Optional!int a = 3;
167     assert(a.isPresent);
168 
169     Optional!(int*) b = null;
170     assert(!b.isPresent);
171 }
172 
173 @safe pure nothrow @nogc unittest
174 {
175     Optional!int a = 3;
176     assert(a.get == 3);
177 }
178 
179 @safe pure /*nothrow*/ unittest
180 {
181     Optional!int a = 3;
182     assert(a.or(4) == 3);
183 
184     Optional!int b = none;
185     assert(b.or(4) == 4);
186 }
187 
188 @safe pure nothrow @nogc unittest
189 {
190     Optional!int a = 3;
191     assert(!a.empty);
192 
193     Optional!int b = none;
194     assert(b.empty);
195 }
196 
197 @safe pure nothrow @nogc unittest
198 {
199     Optional!int a = 3;
200     assert(a.get == 3);
201 }
202 
203 /** Instantiate an `Optional` `value`. */
204 Optional!T optional(T)(T value)
205 {
206     return Optional!T(value);
207 }
208 
209 ///
210 @safe pure nothrow @nogc unittest
211 {
212     Optional!int a = 3;
213     a.popFront();
214     assert(!a.isPresent);
215 }
216 
217 ///
218 @safe pure nothrow @nogc unittest
219 {
220     Optional!int a = 3;
221     assert(a.length == 1);
222 
223     Optional!int b = none;
224     assert(b.length == 0);
225 }
226 
227 ///
228 @safe pure nothrow @nogc unittest
229 {
230     assert(optional(3).isPresent);
231 }
232 
233 ///
234 @trusted pure nothrow @nogc unittest
235 {
236     int i;
237     assert(optional(&i).isPresent);
238     assert(!optional!(int*)(null).isPresent);
239 }
240 
241 ///
242 @safe pure nothrow @nogc unittest
243 {
244     import std.algorithm : map;
245     enum value = 3;
246     assert(optional(value).map!(e => e).front == value);
247 }
248 
249 Optional!T some(T)(T value)
250 in
251 {
252     static if (isNullable!T)
253     {
254         assert(value !is null);
255     }
256 }
257 do
258 {
259     Optional!T o;
260     o.value = value;
261     o.present = true;
262 
263     return o;
264 }
265 
266 ///
267 @safe pure nothrow @nogc unittest
268 {
269     assert(some(3).isPresent);
270 }
271 
272 @safe pure nothrow @nogc None none()
273 {
274     return None();
275 }
276 
277 Optional!T none(T)()
278 {
279     return Optional!T.init;
280 }
281 
282 ///
283 @safe pure nothrow @nogc unittest
284 {
285     assert(!none!int.isPresent);
286 }
287 
288 version (unittest)
289 {
290     // Cannot put this inside the @safe pure nothrow @nogc unittest block due to
291     // https://issues.dlang.org/show_bug.cgi?id=19157
292     private struct Foo
293     {
294         int a;
295         Bar* b;
296 
297         int c(int a)
298         {
299             return a;
300         }
301 
302         Bar d(int a)
303         {
304             return Bar(a);
305         }
306     }
307 
308     private struct Bar
309     {
310         int a;
311 
312         int foo(int a)
313         {
314             return a;
315         }
316     }
317 }