1 /*
2 									__
3 								   / _|
4   __ _ _   _ _ __ ___  _ __ __ _  | |_ ___  ___ ___
5  / _` | | | | '__/ _ \| '__/ _` | |  _/ _ \/ __/ __|
6 | (_| | |_| | | | (_) | | | (_| | | || (_) \__ \__ \
7  \__,_|\__,_|_|  \___/|_|  \__,_| |_| \___/|___/___/
8 
9 Copyright (C) 2018 Ali Akhtarzada
10 Copyright (C) 2018-2020 Aurora Free Open Source Software.
11 Copyright (C) 2018-2020 Luís Ferreira <luis@aurorafoss.org>
12 
13 This file is part of the Aurora Free Open Source Software. This
14 organization promote free and open source software that you can
15 redistribute and/or modify under the terms of the GNU Lesser General
16 Public License Version 3 as published by the Free Software Foundation or
17 (at your option) any later version approved by the Aurora Free Open Source
18 Software Organization. The license is available in the package root path
19 as 'LICENSE' file. Please review the following information to ensure the
20 GNU Lesser General Public License version 3 requirements will be met:
21 https://www.gnu.org/licenses/lgpl.html .
22 
23 Alternatively, this file may be used under the terms of the GNU General
24 Public License version 3 or later as published by the Free Software
25 Foundation. Please review the following information to ensure the GNU
26 General Public License requirements will be met:
27 http://www.gnu.org/licenses/gpl-3.0.html.
28 
29 NOTE: All products, services or anything associated to trademarks and
30 service marks used or referenced on this file are the property of their
31 respective companies/owners or its subsidiaries. Other names and brands
32 may be claimed as the property of others.
33 
34 For more info about intellectual property visit: aurorafoss.org or
35 directly send an email to: contact (at) aurorafoss.org .
36 
37 This file has code parts from 'optional' package which is distributed
38 under MIT License.
39 Check out the project here: https://github.com/aliak00/optional/
40 */
41 
42 /++
43 Extras to std.typecons
44 
45 This file defines extra functions to std.typecons.
46 
47 Authors: Luís Ferreira <luis@aurorafoss.org>
48 Copyright: All rights reserved, Aurora Free Open Source Software
49 License: GNU Lesser General Public License (Version 3, 29 June 2007)
50 Date: 2018-2020
51 +/
52 module aurorafw.stdx.typecons;
53 
54 public import std.typecons;
55 
56 import std.range;
57 import std.format : singleSpec, FormatSpec, formatValue;
58 
59 import aurorafw.stdx.object;
60 import aurorafw.stdx.traits;
61 
62 version (unittest) import aurorafw.unit.assertion;
63 
64 /**
65  * Pattern matching for Nullable and Optional types
66  *
67  * Example:
68  * ---
69  * Optional!int foo = 7;
70  *
71  * foo.match!(
72  *     (int value) => true,
73  *     () => false
74  * ); // true
75  * ---
76  */
77 public template match(handlers...) if (handlers.length <= 2 && handlers.length >= 1)
78 {
79 	MatchReturnType!handlers match(T)(auto ref T t)
80 			if (__traits(isSame, TemplateOf!T, Nullable) || __traits(isSame, TemplateOf!T, Optional))
81 	{
82 		static if (is(typeof(handlers[0](t.get))))
83 		{
84 			alias someHandler = handlers[0];
85 			alias noHandler = handlers[1];
86 		}
87 		else
88 		{
89 			alias someHandler = handlers[1];
90 			alias noHandler = handlers[0];
91 		}
92 
93 		static if (__traits(isSame, TemplateOf!T, Nullable))
94 		{
95 			// Nullable type
96 			if (t.isNull)
97 				return noHandler();
98 			else
99 				return someHandler(t.get());
100 		}
101 		else
102 		{
103 			// Optional type
104 			if (!t.defined)
105 				return noHandler();
106 			else
107 				return someHandler(t.get());
108 		}
109 
110 	}
111 }
112 
113 ///
114 @safe pure
115 @("Optional: pattern matching")
116 unittest
117 {
118 	immutable Optional!int foo = 7;
119 	immutable Optional!int bar;
120 
121 	// dfmt off
122 	assertTrue(foo.match!(
123 		(int value) => true, () => false
124 	));
125 
126 	assertFalse(bar.match!(
127 		(int value) => true, () => false
128 	));
129 	// dfmt on
130 }
131 
132 ///
133 @safe pure
134 @("Nullable: pattern matching")
135 unittest
136 {
137 	immutable Nullable!int foo = 7;
138 	immutable Nullable!int bar;
139 
140 	// dfmt off
141 	assertTrue(foo.match!(
142 		(int value) => true, () => false
143 	));
144 
145 	assertFalse(bar.match!(
146 		(int value) => true, () => false
147 	));
148 	// dfmt on
149 }
150 
151 template isOptional(T)
152 {
153 	import std.traits : isInstanceOf;
154 
155 	enum isOptional = isInstanceOf!(Optional, T);
156 }
157 
158 /**
159  * Defines an optional type
160  *
161  * Optional values are values that can be or not defined.
162  *
163  * Examples:
164  * ---
165  * Optional!int foo; // undefined
166  * foo = 7; // now its defined to 7
167  *
168  * // can also be undefined again
169  * foo.popFront();
170  * ---
171  */
172 struct Optional(T)
173 {
174 
175 	private static string autoReturn(string expression)()
176 	{
177 		return `
178 			auto ref expr() {
179 				return `
180 			~ expression ~ `;
181 			}`
182 			~ q{
183 			alias R = typeof(expr());
184 			static if (!is(R == void))
185 				return empty ? none!R : some!R(expr());
186 			else {
187 				if (!empty) {
188 					expr();
189 				}
190 			}
191 		};
192 	}
193 
194 	private enum isNullInvalid = is(T == class) || is(T == interface)
195 		|| isSomeFunction!T || isPointer!T
196 		|| is(T == typeof(null));
197 
198 	private enum definedIfNotNull = q{
199 		static if (isNullInvalid)
200 			this._defined = this.value.payload !is null;
201 		else
202 			this._defined = true;
203 	};
204 
205 	private union DontCallDestructorT
206 	{
207 		T payload;
208 	}
209 
210 	private DontCallDestructorT value = DontCallDestructorT.init;
211 	private bool _defined = false;
212 
213 	/**
214 	 * Constructs an optional type with a defined given value
215 	 *
216 	 * Params:
217 	 *   value = given value
218 	 */
219 	this(T value)
220 	{
221 		import std.traits : isCopyable;
222 
223 		static if (!isCopyable!T)
224 		{
225 			import std.functional : forward;
226 
227 			this.value.payload = forward!value;
228 		}
229 		else
230 		{
231 			this.value.payload = value;
232 		}
233 
234 		mixin(definedIfNotNull);
235 	}
236 
237 	/**
238 	 * Constructs an optional type with a non defined value
239 	 * Params:
240 	 *   None = None type, empty data
241 	 */
242 	this(const None)
243 	{
244 		this.value = DontCallDestructorT.init;
245 	}
246 
247 	static if (is(T == struct) && hasElaborateDestructor!T)
248 	{
249 		/**
250 		 * Distructs the optional object
251 		 */
252 		~this()
253 		{
254 			if (this._defined)
255 			{
256 				destroy(value.payload);
257 			}
258 		}
259 	}
260 
261 	/**
262 	 * Check if the optional type is empty
263 	 *
264 	 * Returns: true if the value is not defined, false otherwise
265 	 *
266 	 * See_Also: defined()
267 	 */
268 	@property bool empty() const
269 	{
270 		return !this.defined;
271 	}
272 
273 	/**
274 	 * Check if the optional type is defined
275 	 *
276 	 * Returns: true if the value is defined, false otherwise
277 	 *
278 	 * See_Also: empty()
279 	 */
280 	@property bool defined() const
281 	{
282 		return this._defined;
283 	}
284 
285 	/**
286 	 * Attemp to get the defined value
287 	 *
288 	 * Returns: if defined, the defined value, assert otherwise
289 	 */
290 	@property ref inout(T) front() inout
291 	{
292 		assert(defined, "Attempting to get an undefined optional.");
293 		return this.value.payload;
294 	}
295 
296 	/// ditto
297 	@property ref inout(T) get() inout
298 	{
299 		return front;
300 	}
301 
302 	/**
303 	 * Gets the optional value or fallback if no value defined
304 	 *
305 	 * Params:
306 	 *   fallback = fallback value if not defined
307 	 *
308 	 * Returns: if defined, the value, fallback value otherwise
309 	 */
310 	@property inout(T) getOr()(inout(T) fallback) inout
311 	{
312 		return defined ? value.payload : fallback;
313 	}
314 
315 	/// ditto
316 	@property auto getOr(U)(inout(U) fallback) inout
317 	{
318 		return defined ? value.payload : fallback;
319 	}
320 
321 	/**
322 	 * Mark this optional as undefined
323 	 */
324 	void popFront()
325 	{
326 		this._defined = false;
327 	}
328 
329 	/**
330 	 * Compares this optional with an empty None value
331 	 *
332 	 * This will basically tells whether the optional is empty or not.
333 	 *
334 	 * Params:
335 	 *   None = None type, empty data
336 	 * Returns: true if there's a defined value, false otherwise
337 	 */
338 	bool opEquals(const None) const
339 	{
340 		return this.empty;
341 	}
342 
343 	/**
344 	 * Compares this optional with another optional for equality
345 	 *
346 	 * Params:
347 	 *   rhs = right-hand side optional value
348 	 *
349 	 * Returns: true if equal, false otherwise
350 	 */
351 	bool opEquals(U : T)(const auto ref Optional!U rhs) const
352 	{
353 		if ((this.empty || rhs.empty))
354 			return this.empty == rhs.empty;
355 
356 		static if (is(U == class))
357 		{
358 			return this.value.payload is rhs.value.payload;
359 		}
360 		else
361 		{
362 			return this.value.payload == rhs.value.payload;
363 		}
364 	}
365 
366 	/**
367 	 * Compares this optional with a nullable for equality
368 	 *
369 	 * Params:
370 	 *   rhs = right-hand side nullable value
371 	 *
372 	 * Returns: true if equal, false otherwise
373 	 */
374 	bool opEquals(U : T)(auto ref Nullable!U rhs) const
375 	{
376 		return (this.empty || rhs.isNull)
377 			? this.empty == rhs.isNull : this.value.payload == rhs.get;
378 	}
379 
380 	/**
381 	 * Compares this optional with a value for equality
382 	 *
383 	 * Params:
384 	 *   rhs = right-hand side value
385 	 *
386 	 * Returns: true if equal, false otherwise
387 	 */
388 	bool opEquals(U : T)(const auto ref U rhs) const
389 	{
390 		static if (is(U == class) && is(T == class))
391 		{
392 			return defined && this.value.payload is rhs;
393 		}
394 		else
395 		{
396 			return defined && this.value.payload == rhs;
397 		}
398 	}
399 
400 	/**
401 	 * Compare this optional with an input range for equality
402 	 *
403 	 * Params:
404 	 *   rhs = right-hand side value
405 	 *
406 	 * Returns: true if equal, false otherwise
407 	 */
408 	bool opEquals(R)(auto ref R rhs) const
409 	if (isInputRange!R)
410 	{
411 		if (this.empty && rhs.empty)
412 			return true;
413 		if (this.empty || rhs.empty)
414 			return false;
415 
416 		return this.front == rhs.front;
417 	}
418 
419 	/**
420 	 * Assign this optional to a none
421 	 *
422 	 * Params:
423 	 *   None = None type, empty data
424 	 */
425 	auto ref opAssign()(const None) if (isMutable!T)
426 	{
427 		if (!empty)
428 		{
429 			static if (isNullInvalid)
430 			{
431 				this.value.payload = null;
432 			}
433 			else
434 			{
435 				destroy(this.value.payload);
436 			}
437 			this._defined = false;
438 		}
439 		return this;
440 	}
441 
442 	/**
443 	 * Assign this optional to a value
444 	 *
445 	 * Params:
446 	 *   lhs = left-hand value
447 	 */
448 	auto ref opAssign(U : T)(auto ref U lhs) if (isMutable!T && isAssignable!(T, U))
449 	{
450 		import std.algorithm.mutation : moveEmplace, move;
451 
452 		auto copy = DontCallDestructorT(lhs);
453 
454 		if (empty)
455 		{
456 			// trusted since payload is known to be T.init here.
457 			() @trusted { moveEmplace(copy.payload, value.payload); }();
458 		}
459 		else
460 		{
461 			move(copy.payload, value.payload);
462 		}
463 
464 		mixin(definedIfNotNull);
465 		return this;
466 	}
467 
468 	/**
469 	 * Assign this optional to another optional
470 	 *
471 	 * Params:
472 	 *   lhs = left-hand optional value
473 	 */
474 	auto ref opAssign(U : T)(auto ref Optional!U lhs) if (isMutable!T && isAssignable!(T, U))
475 	{
476 		static if (__traits(isRef, lhs) || !isMutable!U)
477 		{
478 			this.value.payload = lhs.value.payload;
479 		}
480 		else
481 		{
482 			import std.algorithm : move;
483 
484 			this.value.payload = move(lhs.value.payload);
485 		}
486 
487 		this._defined = lhs._defined;
488 		return this;
489 	}
490 
491 	/**
492 	 * Unary operator with auto return type
493 	 */
494 	auto opUnary(string op, this This)()
495 	{
496 		mixin(autoReturn!(op ~ "front"));
497 	}
498 
499 	/**
500 	 * Binary operator with auto return types
501 	 *
502 	 * Params:
503 	 *   rhs = right-hand value
504 	 */
505 	auto opBinary(string op, U:
506 			T, this This)(auto ref U rhs)
507 	{
508 		mixin(autoReturn!("front" ~ op ~ "rhs"));
509 	}
510 
511 	/**
512 	 * Binary operator with auto return types
513 	 *
514 	 * Params:
515 	 *   lhs = left-hand value
516 	 */
517 	auto opBinaryRight(string op, U:
518 			T, this This)(auto ref U lhs)
519 	{
520 		mixin(autoReturn!("lhs" ~ op ~ "front"));
521 	}
522 
523 	/**
524 	 * Call operator with auto return types
525 	 *
526 	 * Params:
527 	 *   args = passed arguments
528 	 */
529 	auto opCall(Args...)(Args args) if (from.std.traits.isCallable!T)
530 	{
531 		mixin(autoReturn!(q{this._value(args)}));
532 	}
533 
534 	/**
535 	 * Op Assign operator with auto return types
536 	 * Params:
537 	 *   rhs = right-hand value
538 	 */
539 	auto opOpAssign(string op, U:
540 			T, this This)(auto ref U rhs)
541 	{
542 		mixin(autoReturn!("front" ~ op ~ "= rhs"));
543 	}
544 
545 	static if (isArray!T)
546 	{
547 
548 		/**
549 		 * Index operator with auto return types
550 		 *
551 		 * Params:
552 		 *   index = given index of the array
553 		 */
554 		auto opIndex(this This)(size_t index)
555 		{
556 			enum call = "front[index]";
557 			import std.range : ElementType;
558 
559 			if (empty || index >= front.length || index < 0)
560 			{
561 				return none!(mixin("typeof(" ~ call ~ ")"));
562 			}
563 			mixin(autoReturn!(call));
564 		}
565 
566 		/**
567 		 * Index operator with auto return types
568 		 */
569 		auto opIndex(this This)()
570 		{
571 			mixin(autoReturn!("front[]"));
572 		}
573 
574 		/**
575 		 * Slice operator with auto return types
576 		 *
577 		 * Params:
578 		 *   begin = begin index of the array slice
579 		 *   end = end index of the array slice
580 		 */
581 		auto opSlice(this This)(size_t begin, size_t end)
582 		{
583 			enum call = "front[begin .. end]";
584 			import std.range : ElementType;
585 
586 			if (empty || begin > end || end > front.length)
587 			{
588 				return none!(mixin("typeof(" ~ call ~ ")"));
589 			}
590 			mixin(autoReturn!(call));
591 		}
592 
593 		/**
594 		 * Dollar operator representing the length of the array
595 		 */
596 		auto opDollar() const
597 		{
598 			return empty ? 0 : front.length;
599 		}
600 	}
601 
602 	/**
603 	 * Calculates the hash value of the value inside this optional
604 	 *
605 	 * Returns: hash of the optional value
606 	 */
607 	size_t toHash() const @safe nothrow
608 	{
609 		static if (__traits(compiles, .hashOf(value.payload)))
610 			return defined ? .hashOf(value.payload) : 0;
611 		else
612 			return defined ? typeid(T).getHash(&value.payload) : 0;
613 	}
614 
615 	/**
616 	  * Convert the optional to a human readable string.
617 	  */
618 	string toString()() const
619 	{
620 		import std.conv : to;
621 
622 		if (empty)
623 		{
624 			return "Optional(None)";
625 		}
626 		static if (__traits(compiles, { value.payload.toString; }))
627 		{
628 			return "Some(" ~ value.payload.toString ~ ")";
629 		}
630 		else
631 		{
632 			return "Some(" ~ to!string(value.payload) ~ ")";
633 		}
634 	}
635 
636 }
637 
638 /**
639  * A type with nothing inside used by Optional
640  *
641  * This type is useful to represent empty data from an
642  * optional type, instead of doing payload allocation.
643  */
644 @safe pure nothrow @nogc
645 public struct None
646 {
647 	/**
648 	 * Converts type None to string
649 	 *
650 	 * Returns: None string identifier
651 	 */
652 	@safe pure nothrow @nogc
653 	public string toString() const
654 	{
655 		return __traits(identifier, None);
656 	}
657 }
658 
659 /**
660  * Represents a non defined optional type
661  */
662 @safe pure
663 public auto none(T)()
664 {
665 	return Optional!T();
666 }
667 
668 /**
669  * Represents a None type
670  */
671 @safe pure
672 public None none()
673 {
674 	immutable none = None();
675 	return none;
676 }
677 
678 /**
679  * Creates an optional type with a defined given value
680  *
681  * Params:
682  *   value = a given value
683  */
684 public auto some(T)(auto ref T value)
685 {
686 	import std.traits : isCopyable;
687 
688 	static if (!isCopyable!T)
689 	{
690 		import std.functional : forward;
691 
692 		return optional!T(forward!value);
693 	}
694 	else
695 	{
696 		return optional!T(value);
697 	}
698 }
699 
700 /// ditto
701 public auto optional(T)(auto ref T value)
702 {
703 	import std.traits : isCopyable;
704 
705 	static if (!isCopyable!T)
706 	{
707 		import std.functional : forward;
708 
709 		return Optional!T(forward!value);
710 	}
711 	else
712 	{
713 		return Optional!T(value);
714 	}
715 }
716 
717 /**
718  * Creates an optional type with a none
719  * Params:
720  *   none = None type, empty data
721  */
722 public auto some(T)(const None none)
723 {
724 	return optional!T(none);
725 }
726 
727 /// ditto
728 public auto optional(T)(const None none)
729 {
730 	return Optional!T(none);
731 }
732 
733 /**
734  * Creates an empty optional type
735  */
736 public auto optional(T)()
737 {
738 	return Optional!T();
739 }
740 
741 ///
742 @system pure
743 @("Optional: front & popFront")
744 unittest
745 {
746 	auto a = some(7);
747 	auto b = none!int;
748 
749 	assertEquals(7, a.front);
750 	assertTrue(a.defined);
751 	a.popFront();
752 	assertFalse(a.defined);
753 
754 	expectThrows!AssertError(a.front);
755 	expectThrows!AssertError(b.front);
756 }
757 
758 ///
759 @safe pure
760 @("Optional: definition")
761 unittest
762 {
763 	assertTrue(some(5).defined);
764 	assertTrue(Optional!int(5).defined);
765 
766 	struct foobar
767 	{
768 		@disable this(this);
769 
770 		int z;
771 	}
772 
773 	assertTrue(some(foobar()).defined);
774 	assertTrue(some(none!int).defined);
775 
776 	assertFalse(some!int(none).defined);
777 	assertFalse(some(null).defined);
778 
779 	assertFalse(none!int.defined);
780 	assertFalse(Optional!int().defined);
781 	assertFalse(Optional!(int[])().defined);
782 }
783 
784 /**
785  * Converts a input range into an optional type
786  *
787  * Params:
788  *   range = given input range
789  */
790 auto toOptional(R)(auto ref R range) if (isInputRange!R || is(R == void[]))
791 {
792 	static if (is(R == void[]))
793 	{
794 		return none;
795 	}
796 	else
797 	{
798 		assert(range.empty || range.walkLength == 1);
799 		if (range.empty)
800 		{
801 			return none!(ElementType!R);
802 		}
803 		else
804 		{
805 			return some(range.front);
806 		}
807 	}
808 }
809 
810 /**
811  * Converts a nullable type to an optional type
812  *
813  * Params:
814  *   nullable = given Nullable!T
815  */
816 auto toOptional(T)(auto inout ref Nullable!T nullable)
817 {
818 	if (nullable.isNull)
819 	{
820 		return inout Optional!T();
821 	}
822 	else
823 	{
824 		return inout Optional!T(nullable.get);
825 	}
826 }
827 
828 ///
829 @safe pure
830 @("Optional: toOptional")
831 unittest
832 {
833 	assertEquals(some(7), toOptional([7]));
834 	assertEquals(none, toOptional([]));
835 	assertEquals(none, toOptional([null]));
836 	assertEquals(none, toOptional((int[]).init));
837 
838 	assertEquals(some(7), toOptional(Nullable!(int)(7)));
839 	assertEquals(none, toOptional(Nullable!(typeof(null))(null)));
840 	assertEquals(none, toOptional(Nullable!(int)()));
841 }
842 
843 ///
844 @safe pure
845 @("Optional: assign operator")
846 unittest
847 {
848 	Optional!int foo;
849 	Optional!int bar;
850 	assertEquals(none, foo);
851 	foo = none;
852 	assertEquals(none, foo);
853 	foo = bar;
854 	assertEquals(none, bar);
855 	assertEquals(bar, foo);
856 	foo = 7;
857 	assertEquals(7, foo);
858 	// assign again to move instead of moveEmplace
859 	foo = 3;
860 	assertEquals(3, foo);
861 	foo = some(7);
862 	assertEquals(7, foo);
863 	// assign again to none invalidation defined value
864 	foo = none;
865 	assertEquals(none, foo);
866 
867 }
868 
869 ///
870 @safe pure
871 @("Optional: arrays")
872 unittest
873 {
874 	auto arr = some([1, 2, 3, 45]);
875 	assertEquals(2, arr[1]);
876 	assertEquals([1, 2, 3, 45], arr[]);
877 	assertEquals([1, 2, 3, 45], arr[0 .. $]);
878 	auto jk = none!(int[]);
879 	assertEquals(none!int, jk[213]);
880 	assertEquals(none!(int[]), jk[]);
881 	assertEquals(none!(int[]), jk[0 .. $]);
882 }
883 
884 ///
885 @safe pure
886 @("Optional: toHash")
887 unittest
888 {
889 	Optional!int foo;
890 	assertEquals(0, foo.toHash);
891 	foo = 7;
892 	assertTrue(foo.toHash > 0);
893 	assertEquals(.hashOf(7), foo.toHash);
894 }
895 
896 ///
897 @safe
898 @("Optional: inpure toHash")
899 unittest
900 {
901 	Optional!Object bar;
902 	assertEquals(0, bar.toHash);
903 	auto obj = new Object();
904 	bar = obj;
905 	assertTrue(bar.toHash > 0);
906 	assertEquals(.hashOf(obj), bar.toHash);
907 }
908 
909 ///
910 @system
911 @("Optional: inpure and unsafe toHash")
912 unittest
913 {
914 	auto obj = new Object();
915 	Optional!Object bar = obj;
916 	assertEquals(typeid(Object).getHash(&obj), bar.toHash);
917 }
918 
919 ///
920 @safe pure
921 @("Optional: opEquals")
922 unittest
923 {
924 	assertEquals(7, some(7));
925 	assertEquals(none!int, none!int);
926 	assertEquals(none, none!int);
927 	assertEquals(none, none);
928 	assertEquals(none!int, some(none!int));
929 	assertEquals(none, some(none));
930 
931 	// compare with nullable
932 	assertEquals(nullable(7), optional(7));
933 	assertEquals(Nullable!(int).init, none!int);
934 
935 	// range opEquals
936 	assertEquals([7], some(7));
937 	int[] a = [];
938 	assertEquals(a, none!int);
939 }
940 
941 ///
942 @safe pure
943 @("Optional: class opEquals")
944 unittest
945 {
946 	@safe pure class C
947 	{
948 		int c;
949 
950 		// assertEquals needs @safe and pure
951 		// toString method in case of fail
952 		@safe pure
953 		override string toString() const
954 		{
955 			import std.conv : to;
956 
957 			return c.to!string;
958 		}
959 	}
960 
961 	C c = new C();
962 	assertEquals(c, optional(c));
963 	assertEquals(optional(c), optional(c));
964 	// cover toString method
965 	assertEquals("0", optional(c).front.toString);
966 }
967 
968 ///
969 @safe pure
970 @("Optional: toString")
971 unittest
972 {
973 	assertEquals("Some(1)", some(1).toString);
974 	assertEquals("None", none.toString);
975 	assertEquals("Optional(None)", none!int.toString);
976 }
977 
978 /**
979  * Converts an optional type to a nullable type
980  *
981  * Params:
982  *   opt = optional
983  */
984 auto toNullable(T)(auto ref Optional!T opt)
985 {
986 	return (opt.empty)
987 		? Nullable!T() : opt.front.nullable;
988 }
989 
990 ///
991 @safe pure
992 @("Optional: toNullable")
993 unittest
994 {
995 	assertEquals(nullable(7), some(7).toNullable);
996 	assertEquals(Nullable!(int).init, none!int.toNullable);
997 }
998 
999 ///
1000 @safe pure
1001 @("Optional: getOr")
1002 unittest
1003 {
1004 	auto a = some(3);
1005 	auto b = none!int;
1006 
1007 	assertEquals(3, a.getOr(7));
1008 	assertEquals(8, b.getOr(8));
1009 }
1010 
1011 /**
1012  * Gets the value inside of a nullable type or fallback to a given value
1013  *
1014  * Params:
1015  *   n = nullable type
1016  *   t = fallback value
1017  *
1018  * Returns: value inside nullable if defined, fallback otherwise
1019  */
1020 T getOr(T)(Nullable!T n, T t)
1021 {
1022 	return n.isNull ? t : n.get();
1023 }
1024 
1025 ///
1026 @safe pure
1027 @("Nullable: getOr")
1028 unittest
1029 {
1030 	auto a = nullable(3);
1031 	auto b = Nullable!(int).init;
1032 
1033 	assertEquals(3, a.getOr(7));
1034 	assertEquals(8, b.getOr(8));
1035 }
1036 
1037 ///
1038 @safe pure
1039 @("Optional: autoReturn")
1040 unittest
1041 {
1042 	auto foobar = () => "thestring";
1043 	// type independent
1044 	immutable string expr1 = Optional!(string).autoReturn!("foobar()");
1045 	immutable string expr2 = Optional!(typeof(null)).autoReturn!("foobar()");
1046 
1047 	import std..string : splitLines, strip, startsWith;
1048 	import std.algorithm.iteration : map, filter, joiner;
1049 
1050 	auto minify = (string t) => t.splitLines
1051 		.map!(strip)
1052 		.filter!(l => !l.empty)
1053 		.filter!(l => !l.startsWith("//"))
1054 		.joiner;
1055 
1056 	auto minifiedExpr = minify(q{
1057 		auto ref expr() {
1058 				return foobar();
1059 			}
1060 			alias R = typeof(expr());
1061 			static if (!is(R == void))
1062 				return empty ? none!R : some!R(expr());
1063 			else {
1064 				if (!empty) {
1065 					expr();
1066 				}
1067 			}
1068 	});
1069 
1070 	// just to make sure you dont make mistakes on minify
1071 	assertFalse(minifiedExpr.array.empty);
1072 	assertFalse(minify(expr1).array.empty);
1073 	assertFalse(minify(expr2).array.empty);
1074 
1075 	assertEquals(minifiedExpr.array, minify(expr1).array);
1076 	assertEquals(minifiedExpr.array, minify(expr2).array);
1077 }
1078 
1079 struct OptionalChain(T)
1080 {
1081 	import std.traits : hasMember;
1082 
1083 	private static string autoReturn(string expression)()
1084 	{
1085 		return `
1086 			auto ref expr() {
1087 				return `
1088 			~ expression ~ `;
1089 			}
1090 			`
1091 			~ q{
1092 			auto ref val() {
1093 				// If the dispatched result is an Optional itself, we flatten it out so that client code
1094 				// does not have to do a.oc.member.oc.otherMember
1095 				static if (isOptional!(typeof(expr()))) {
1096 					return expr().front;
1097 				} else {
1098 					return expr();
1099 				}
1100 			}
1101 			alias R = typeof(val());
1102 			static if (is(R == void)) {
1103 				if (!value.empty) {
1104 					val();
1105 				}
1106 			} else {
1107 				if (value.empty) {
1108 					return OptionalChain!R(none!R());
1109 				}
1110 				static if (isOptional!(typeof(expr()))) {
1111 					// If the dispatched result is an optional, check if the expression is empty before
1112 					// calling val() because val() calls .front which would assert if empty.
1113 					if (expr().empty) {
1114 						return OptionalChain!R(none!R());
1115 					}
1116 				}
1117 				return OptionalChain!R(some(val()));
1118 			}
1119 		};
1120 	}
1121 
1122 	public Optional!T value;
1123 	alias value this;
1124 
1125 	this(Optional!T value)
1126 	{
1127 		this.value = value;
1128 	}
1129 
1130 	this(T value)
1131 	{
1132 		this.value = value;
1133 	}
1134 
1135 	public string toString()
1136 	{
1137 		return value.toString;
1138 	}
1139 
1140 	public template opDispatch(string name) if (hasMember!(T, name))
1141 	{
1142 		static if (is(typeof(__traits(getMember, T, name)) == function))
1143 		{
1144 			auto opDispatch(Args...)(auto ref Args args)
1145 			{
1146 				mixin(autoReturn!("value.front." ~ name ~ "(args)"));
1147 			}
1148 		}
1149 		else static if (is(typeof(mixin("value.front." ~ name))))
1150 		{
1151 			// non-function field
1152 			auto opDispatch(Args...)(auto ref Args args)
1153 			{
1154 				static if (Args.length == 0)
1155 				{
1156 					mixin(autoReturn!("value.front." ~ name));
1157 				}
1158 				else static if (Args.length == 1)
1159 				{
1160 					mixin(autoReturn!("value.front." ~ name ~ " = args[0]"));
1161 				}
1162 				else
1163 				{
1164 					static assert(
1165 							0,
1166 							"Dispatched " ~ T.stringof ~ "." ~ name ~ " was resolved to non-function field that has more than one argument",
1167 					);
1168 				}
1169 			}
1170 		}
1171 		else
1172 		{
1173 			// member template
1174 			template opDispatch(Ts...)
1175 			{
1176 				enum targs = Ts.length ? "!Ts" : "";
1177 				auto opDispatch(Args...)(auto ref Args args)
1178 				{
1179 					mixin(autoReturn!("value.front." ~ name ~ targs ~ "(args)"));
1180 				}
1181 			}
1182 		}
1183 	}
1184 }
1185 
1186 auto oc(T)(auto ref T value) if (isNullTestable!T && !isInstanceOf!(Nullable, T))
1187 {
1188 	return OptionalChain!T(value);
1189 }
1190 
1191 auto oc(T)(auto ref Optional!T value)
1192 {
1193 	return OptionalChain!T(value);
1194 }
1195 
1196 auto oc(T)(auto ref Nullable!T value)
1197 {
1198 	return OptionalChain!T(value.isNull ? none!T : some(value.get));
1199 }
1200 
1201 @safe pure
1202 @("OptionalChain: Optional")
1203 unittest
1204 {
1205 	@safe pure class C
1206 	{
1207 		int fun()
1208 		{
1209 			return 3;
1210 		}
1211 	}
1212 
1213 	Optional!C a = null;
1214 	assertEquals(none!int, oc(a).fun);
1215 
1216 	a = new C();
1217 	assertEquals(3, oc(a).fun);
1218 	assertEquals(some(3), oc(a).fun);
1219 }
1220 
1221 @safe pure
1222 @("OptionalChain: Nullable")
1223 unittest
1224 {
1225 	@safe pure class C
1226 	{
1227 		int fun()
1228 		{
1229 			return 3;
1230 		}
1231 	}
1232 
1233 	Nullable!C a;
1234 	assertEquals(none!int, oc(a).fun);
1235 
1236 	a = new C();
1237 	assertEquals(3, oc(a).fun);
1238 	assertEquals(some(3), oc(a).fun);
1239 }
1240 
1241 @safe pure
1242 @("OptionalChain: null testable values")
1243 unittest
1244 {
1245 	// not null testable values (basic types and structs)
1246 	assertFalse(__traits(compiles, oc(7)));
1247 
1248 	// nullable c
1249 	@safe pure
1250 	class C
1251 	{
1252 		int c = 3;
1253 		alias c this;
1254 
1255 		// need this to not be ambigous
1256 		@safe pure
1257 		override string toString() const
1258 		{
1259 			import std.conv : to;
1260 
1261 			return c.to!string;
1262 		}
1263 	}
1264 
1265 	C c = null;
1266 	assertTrue(none!C == oc(c).value);
1267 	assertEquals(none!int, oc(c).c);
1268 
1269 	c = new C();
1270 
1271 	assertEquals(c, oc(c).value);
1272 	assertEquals(3, oc(c).c);
1273 	// just to cover toString
1274 	assertEquals("Some(3)", oc(c).toString);
1275 }
1276 
1277 ///
1278 @safe pure
1279 @("OptionalChain: autoReturn")
1280 unittest
1281 {
1282 	// type independent
1283 	immutable string expr1 = OptionalChain!(string).autoReturn!("value.front");
1284 	immutable string expr2 = OptionalChain!(typeof(null)).autoReturn!("value.front");
1285 
1286 	import std..string : splitLines, strip, startsWith;
1287 	import std.algorithm.iteration : map, filter, joiner;
1288 
1289 	auto minify = (string t) => t.splitLines
1290 		.map!(strip)
1291 		.filter!(l => !l.empty)
1292 		.filter!(l => !l.startsWith("//"))
1293 		.joiner;
1294 
1295 	auto minifiedExpr = minify(q{
1296 			auto ref expr() {
1297 				return value.front;
1298 			}
1299 
1300 			auto ref val() {
1301 				static if (isOptional!(typeof(expr()))) {
1302 					return expr().front;
1303 				} else {
1304 					return expr();
1305 				}
1306 			}
1307 			alias R = typeof(val());
1308 			static if (is(R == void)) {
1309 				if (!value.empty) {
1310 					val();
1311 				}
1312 			} else {
1313 				if (value.empty) {
1314 					return OptionalChain!R(none!R());
1315 				}
1316 				static if (isOptional!(typeof(expr()))) {
1317 					if (expr().empty) {
1318 						return OptionalChain!R(none!R());
1319 					}
1320 				}
1321 				return OptionalChain!R(some(val()));
1322 			}
1323 	});
1324 
1325 	// just to make sure you dont make mistakes on minify
1326 	assertFalse(minifiedExpr.array.empty);
1327 	assertFalse(minify(expr1).array.empty);
1328 	assertFalse(minify(expr2).array.empty);
1329 
1330 	assertEquals(minifiedExpr.array, minify(expr1).array);
1331 	assertEquals(minifiedExpr.array, minify(expr2).array);
1332 }