1 /++
2 This module is implementing the Expected idiom.
3 
4 See the [Andrei Alexandrescu’s talk (Systematic Error Handling in C++](http://channel9.msdn.com/Shows/Going+Deep/C-and-Beyond-2012-Andrei-Alexandrescu-Systematic-Error-Handling-in-C)
5 and [its slides](https://skydrive.live.com/?cid=f1b8ff18a2aec5c5&id=F1B8FF18A2AEC5C5!1158).
6 
7 Or more recent ["Expect the Expected"](https://www.youtube.com/watch?v=nVzgkepAg5Y) by Andrei Alexandrescu for further background.
8 
9 It is also inspired by C++'s proposed [std::expected](https://wg21.link/p0323) and [Rust's](https://www.rust-lang.org/) [Result](https://doc.rust-lang.org/std/result/).
10 
11 Similar work is [expectations](http://code.dlang.org/packages/expectations) by Paul Backus.
12 
13 Main differences with that are:
14 
15 $(LIST
16 * lightweight, no other external dependencies
17 * allows to use same types for `T` and `E`
18 * allows to define $(LREF Expected) without value (`void` for `T`)
19 * provides facility to change the $(LREF Expected) behavior by custom `Hook` implementation using the Design by introspection.
20 * can enforce result check (with a cost)
21 )
22 
23 Default type for error is `string`, i.e. `Expected!int` is the same as `Expected!(int, string)`
24 
25 $(LREF Expected) has customizable behavior with the help of a third type parameter,
26 `Hook`. Depending on what methods `Hook` defines, core operations on the
27 $(LREF Expected) may be verified or completely redefined.
28 If `Hook` defines no method at all and carries no state, there is no change in
29 default behavior.
30 This module provides a few predefined hooks (below) that add useful behavior to
31 $(LREF Expected):
32 
33 $(BOOKTABLE ,
34     $(TR $(TD $(LREF Abort)) $(TD
35         fails every incorrect operation with a call to `assert(0)`.
36         It is the default third parameter, i.e. $(D Expected!short) is the same as
37         $(D Expected!(short, string, Abort)).
38     ))
39     $(TR $(TD $(LREF Throw)) $(TD
40         fails every incorrect operation by throwing an exception.
41     ))
42 )
43 
44 The hook's members are looked up statically in a Design by Introspection manner
45 and are all optional. The table below illustrates the members that a hook type
46 may define and their influence over the behavior of the `Checked` type using it.
47 In the table, `hook` is an alias for `Hook` if the type `Hook` does not
48 introduce any state, or an object of type `Hook` otherwise.
49 
50 $(TABLE_ROWS
51     * + Hook member
52       + Semantics in Expected!(T, E, Hook)
53     * - `enableDefaultConstructor`
54       - If defined, $(LREF Expected) would have enabled or disabled default constructor based on it's `bool` value. Default constructor is disabled by default. `opAssign` for value and error types is generated if default constructor is enabled.
55     * - `enableCopyConstructor`
56       - If defined, $(LREF Expected) would have enabled or disabled copy constructor based on it's `bool` value. It is enabled by default. When disabled, it enables automatic check if the result was checked either for value or error. When not checked it calls $(D hook.onUnchecked) if provided.
57 
58         $(NOTE WARNING: As currently it's not possible to change internal state of `const` or `immutable` object, automatic checking would't work on these. Hopefully with `__mutable` proposal..)
59     * - `onAccessEmptyValue`
60       - If value is accessed on unitialized $(LREF Expected) or $(LREF Expected) with error value, $(D hook.onAccessEmptyValue!E(err)) is called. If hook doesn't implement the handler, `T.init` is returned.
61     * - `onAccessEmptyError`
62       - If error is accessed on unitialized $(LREF Expected) or $(LREF Expected) with value, $(D hook.onAccessEmptyError()) is called. If hook doesn't implement the handler, `E.init` is returned.
63     * - `onUnchecked`
64       - If the result of $(LREF Expected) isn't checked, $(D hook.onUnchecked()) is called to handle the error. If hook doesn't implement the handler, assert is thrown.
65         $(NOTE Note that `hook.enableCopyConstructor` must be false for checks to work.)
66 )
67 
68 License: BSL-1.0
69 Author: Tomáš Chaloupka
70 +/
71 
72 //TODO: collect errno function call - see https://dlang.org/phobos/std_exception.html#ErrnoException
73 //TODO: ability to enforce error handling (via refcounted payload)
74 
75 module expected;
76 
77 /// Basic usage
78 @safe unittest
79 {
80 	auto foo(int i) {
81 		if (i == 0) return unexpected!int("oops");
82 		return expected(42 / i);
83 	}
84 
85 	auto bar(int i) {
86 		if (i == 0) throw new Exception("err");
87 		return i-1;
88 	}
89 
90 	// basic checks
91 	assert(foo(2));
92 	assert(foo(2).hasValue);
93 	assert(!foo(2).hasError);
94 	assert(foo(2).value == 21);
95 
96 	assert(!foo(0));
97 	assert(!foo(0).hasValue);
98 	assert(foo(0).hasError);
99 	assert(foo(0).error == "oops");
100 
101 	// void result
102 	assert(Expected!(void)()); // no error -> success
103 	assert(!Expected!(void)().hasError);
104 	// assert(unexpected("foo").value); // doesn't have hasValue and value properties
105 
106 	// expected from throwing function
107 	assert(expected!bar(1) == 0);
108 	assert(expected!bar(0).error.msg == "err");
109 
110 	// orElse
111 	assert(foo(2).orElse!(() => 0) == 21);
112 	assert(foo(0).orElse(100) == 100);
113 
114 	// andThen
115 	assert(foo(2).andThen(foo(6)) == 7);
116 	assert(foo(0).andThen(foo(6)).error == "oops");
117 
118 	// map
119 	assert(foo(2).map!(a => a*2).map!(a => a - 2) == 40);
120 	assert(foo(0).map!(a => a*2).map!(a => a - 2).error == "oops");
121 
122 	// mapError
123 	assert(foo(0).mapError!(e => "OOPS").error == "OOPS");
124 	assert(foo(2).mapError!(e => "OOPS") == 21);
125 
126 	// mapOrElse
127 	assert(foo(2).mapOrElse!(v => v*2, e => 0) == 42);
128 	assert(foo(0).mapOrElse!(v => v*2, e => 0) == 0);
129 }
130 
131 version (unittest) {
132 	import std.algorithm : reverse;
133 	import std.exception : assertThrown;
134 	import std.stdio : writeln;
135 }
136 
137 @safe:
138 
139 /++
140 	`Expected!(T, E)` is a type that represents either success or failure.
141 
142 	Type `T` is used for success value.
143 	If `T` is `void`, then $(LREF Expected) can only hold error value and is considered a success when there is no error value.
144 
145 	Type `E` is used for error value.
146 	The default type for the error value is `string`.
147 
148 	Default behavior of $(LREF Expected) can be modified by the `Hook` template parameter.
149 
150 	Params:
151 		T    = represents the expected value
152 		E    = represents the reason explaining why it doesn’t contains avalue of type T, that is the unexpected value.
153 		Hook = defines the $(LREF Expected) type behavior
154 +/
155 struct Expected(T, E = string, Hook = Abort)
156 	if (!is(E == void))
157 {
158 	import std.functional: forward;
159 	import std.meta : AliasSeq, Erase, NoDuplicates;
160 	import std.traits: isAssignable, isCopyable, Unqual;
161 	import std.typecons : Flag, No, Yes;
162 
163 	private alias Types = NoDuplicates!(Erase!(void, AliasSeq!(T, E)));
164 
165 	static foreach (i, T; Types)
166 	{
167 		/++
168 			Constructs an $(LREF Expected) with value or error based on the tye of the provided.
169 
170 			In case when `T == E`, it constructs $(LREF Expected) with value.
171 
172 			In case when `T == void`, it constructs $(LREF Expected) with error value.
173 
174 			Default constructor (if enabled) initializes $(LREF Expected) to `T.init` value.
175 			If `T == void`, it initializes $(LREF Expected) with no error.
176 		+/
177 		this()(auto ref T val)
178 		{
179 			static if (isCopyable!T) storage = Storage(val);
180 			else storage = Storage(forward!val);
181 			setState!(T, Yes.force)();
182 		}
183 
184 		static if (isCopyable!T)
185 		{
186 			/// ditto
187 			this()(auto ref const(T) val) const
188 			{
189 				storage = const(Storage)(val);
190 				setState!(T, Yes.force)();
191 			}
192 
193 			/// ditto
194 			this()(auto ref immutable(T) val) immutable
195 			{
196 				storage = immutable(Storage)(val);
197 				setState!(T, Yes.force)();
198 			}
199 		}
200 		else
201 		{
202 			@disable this(const(T) val) const;
203 			@disable this(immutable(T) val) immutable;
204 		}
205 	}
206 
207 	// generate constructor with flag to determine type of value
208 	static if (Types.length == 1 && !is(T == void))
209 	{
210 		/++ Constructs an $(LREF Expected) with value or error based on the provided flag.
211 			This constructor is available only for cases when value and error has the same type,
212 			so we can still construct $(LREF Expected) with value or error.
213 
214 			Params:
215 				val     = Value to set as value or error
216 				success = If `true`, $(LREF Expected) with value is created, $(LREF Expected) with error otherwise.
217 		+/
218 		this()(auto ref E val, bool success)
219 		{
220 			static if (isCopyable!T) storage = Storage(val);
221 			else storage = Storage(forward!val);
222 			setState!E(success ? State.value : State.error);
223 		}
224 
225 		static if (isCopyable!E)
226 		{
227 			/// ditto
228 			this()(auto ref const(E) val, bool success) const
229 			{
230 				storage = const(Storage)(val);
231 				setState!E(success ? State.value : State.error);
232 			}
233 
234 			/// ditto
235 			this()(auto ref immutable(E) val, bool success) immutable
236 			{
237 				storage = immutable(Storage)(val);
238 				setState!E(success ? State.value : State.error);
239 			}
240 		}
241 		else
242 		{
243 			@disable this(const(E) val, bool success) const;
244 			@disable this(immutable(E) val, bool success) immutable;
245 		}
246 	}
247 
248 	static if (!is(T == void) && !isDefaultConstructorEnabled!Hook) @disable this();
249 
250 	static if (!isCopyConstructorEnabled!Hook) @disable this(this);
251 	static if (isChecked!Hook)
252 	{
253 		~this()
254 		{
255 			if (!checked)
256 			{
257 				static if (hasOnUnchecked!Hook) __traits(getMember, Hook, "onUnchecked")();
258 				else assert(0, "unchecked result");
259 			}
260 		}
261 	}
262 
263 	static if (isDefaultConstructorEnabled!Hook)
264 	{
265 		static foreach (i, CT; Types)
266 		{
267 			static if (isAssignable!CT)
268 			{
269 				/++ Assigns a value or error to an $(LREF Expected).
270 
271 					Note: This is only allowed when default constructor is also enabled.
272 				+/
273 				void opAssign()(auto ref CT rhs)
274 				{
275 					//TODO: Hook to disallow reassign
276 					storage = Storage(forward!rhs);
277 					setState!CT();
278 				}
279 			}
280 		}
281 	}
282 
283 	//damn these are ugly :(
284 	static if (!isChecked!Hook) {
285 		/++ Implicit conversion to bool.
286 			Returns: `true` if there is no error set, `false` otherwise.
287 		+/
288 		bool opCast(T)() const if (is(T == bool)) { return !this.hasError; }
289 	} else {
290 		/// ditto
291 		bool opCast(T)() if (is(T == bool)) { return !this.hasError; }
292 	}
293 
294 	static if (!is(T == void))
295 	{
296 		static if (!isChecked!Hook) {
297 			/++ Checks whether this $(LREF Expected) object contains a specific expected value.
298 
299 				* `opEquals` for the value is available only when `T != void`.
300 				* `opEquals` for the error isn't available, use equality test for $(LREF Expected) in that case.
301 			+/
302 			bool opEquals()(const auto ref T rhs) const
303 			{
304 				return hasValue && value == rhs;
305 			}
306 		} else {
307 			/// ditto
308 			bool opEquals()(auto ref T rhs) { return hasValue && value == rhs; }
309 		}
310 	}
311 
312 	static if (!isChecked!Hook) {
313 		/// Checks whether this $(LREF Expected) object and `rhs` contain the same expected value or error value.
314 		bool opEquals()(const auto ref Expected!(T, E, Hook) rhs) const
315 		{
316 			if (state != rhs.state) return false;
317 			static if (!is(T == void)) { if (hasValue) return value == rhs.value; }
318 			return error == rhs.error;
319 		}
320 	} else {
321 		/// ditto
322 		bool opEquals()(auto ref Expected!(T, E, Hook) rhs)
323 		{
324 			if (state != rhs.state) return false;
325 			static if (!is(T == void)) { if (hasValue) return value == rhs.value; }
326 			return error == rhs.error;
327 		}
328 	}
329 
330 	static if (!isChecked!Hook) {
331 		/++ Calculates the hash value of the $(LREF Expected) in a way that iff it has a value,
332 			it returns hash of the value.
333 			Hash is computed using internal state and storage of the $(LREF Expected) otherwise.
334 		+/
335 		size_t toHash()() const nothrow
336 		{
337 			static if (!is(T == void)) { if (hasValue) return value.hashOf; }
338 			return storage.hashOf(state);
339 		}
340 	} else {
341 		/// ditto
342 		size_t toHash()() nothrow
343 		{
344 			static if (!is(T == void)) { if (hasValue) return value.hashOf; }
345 			return storage.hashOf(state);
346 		}
347 	}
348 
349 	static if (!is(T == void))
350 	{
351 		static if (!isChecked!Hook) {
352 			/// Checks if $(LREF Expected) has value
353 			@property bool hasValue()() const { return state == State.value; }
354 		}
355 		else {
356 			/// ditto
357 			@property bool hasValue()()
358 			{
359 				static if (!isCopyConstructorEnabled!Hook) checked = true;
360 				return state == State.value;
361 			}
362 		}
363 
364 		static if (!isChecked!Hook) {
365 			/++
366 				Returns the expected value if there is one.
367 
368 				With default `Abort` hook, it asserts when there is no value.
369 				It calls hook's `onAccessEmptyValue` otherwise.
370 
371 				It returns `T.init` when hook doesn't provide `onAccessEmptyValue`.
372 			+/
373 			@property auto ref inout(T) value() inout
374 			{
375 				if (state != State.value)
376 				{
377 					static if (hasOnAccessEmptyValue!(Hook, E))
378 						__traits(getMember, Hook, "onAccessEmptyValue")(state == State.error ? trustedGetError() : E.init);
379 					else return T.init;
380 				}
381 				return trustedGetValue();
382 			}
383 		} else {
384 			@property auto ref T value()
385 			{
386 				static if (!isCopyConstructorEnabled!Hook) checked = true;
387 
388 				if (state != State.value)
389 				{
390 					static if (hasOnAccessEmptyValue!(Hook, E))
391 						__traits(getMember, Hook, "onAccessEmptyValue")(state == State.error ? trustedGetError() : E.init);
392 					else return T.init;
393 				}
394 				return trustedGetValue();
395 			}
396 		}
397 	}
398 
399 	static if (!isChecked!Hook) {
400 		/// Checks if $(LREF Expected) has error
401 		@property bool hasError()() const { return state == State.error; }
402 	} else {
403 		/// ditto
404 		@property bool hasError()()
405 		{
406 			static if (!isCopyConstructorEnabled!Hook) checked = true;
407 			return state == State.error;
408 		}
409 	}
410 
411 	static if (!isChecked!Hook) {
412 		/++
413 			Returns the error value. May only be called when `hasValue` returns `false`.
414 
415 			If there is no error value, it calls hook's `onAccessEmptyError`.
416 
417 			It returns `E.init` when hook doesn't provide `onAccessEmptyError`.
418 		+/
419 		@property auto ref inout(E) error() inout
420 		{
421 			if (state != State.error)
422 			{
423 				static if (hasOnAccessEmptyError!Hook) __traits(getMember, Hook, "onAccessEmptyError")();
424 				else return E.init;
425 			}
426 			return trustedGetError;
427 		}
428 	} else {
429 		@property auto ref E error()
430 		{
431 			static if (!isCopyConstructorEnabled!Hook) checked = true;
432 
433 			if (state != State.error)
434 			{
435 				static if (hasOnAccessEmptyError!Hook) __traits(getMember, Hook, "onAccessEmptyError")();
436 				else return E.init;
437 			}
438 			return trustedGetError;
439 		}
440 	}
441 
442 	// range interface
443 	static if (!is(T == void))
444 	{
445 		static if (!isChecked!Hook) {
446 			/++ Range interface defined by `empty`, `front`, `popFront`.
447 				Yields one value if $(LREF Expected) has value.
448 
449 				If `T == void`, range interface isn't defined.
450 			+/
451 			@property bool empty() const { return state != State.value; }
452 
453 			/// ditto
454 			@property auto ref inout(T) front() inout { return value; }
455 		} else {
456 			@property bool empty() { checked = true; return state != State.value; }
457 
458 			/// ditto
459 			@property auto ref T front() { return value; }
460 		}
461 
462 		/// ditto
463 		void popFront() { state = State.empty; }
464 	}
465 
466 	private:
467 
468 	//FIXME: can probably be union instead, but that doesn't work well with destructors and copy constructors/postblits
469 	//or change it for a couple of pointers and make the Expected payload refcounted
470 	//that could be used to enforce result check too
471 	struct Storage
472 	{
473 		Types values;
474 
475 		// generate storage constructors
476 		static foreach (i, CT; Types)
477 		{
478 			@trusted this()(auto ref CT val)
479 			{
480 				static if (isCopyable!CT) __traits(getMember, Storage, "values")[i] = val;
481 				else __traits(getMember, Storage, "values")[i] = forward!val;
482 			}
483 
484 			static if (isCopyable!CT)
485 			{
486 				@trusted this()(auto ref const(CT) val) const { __traits(getMember, Storage, "values")[i] = val; }
487 				@trusted this()(auto ref immutable(CT) val) immutable { __traits(getMember, Storage, "values")[i] = val; }
488 			}
489 			else
490 			{
491 				@disable this(const(CT) val) const;
492 				@disable this(immutable(CT) val) immutable;
493 			}
494 		}
495 	}
496 
497 	//@trusted // needed for union
498 	ref inout(E) trustedGetError()() inout
499 	{
500 		static if (Types.length == 1) return __traits(getMember, storage, "values")[0];
501 		else return __traits(getMember, storage, "values")[1];
502 	}
503 
504 	static if (!is(T == void))
505 	{
506 		//@trusted // needed for union
507 		ref inout(T) trustedGetValue()() inout
508 		{
509 			return __traits(getMember, storage, "values")[0];
510 		}
511 	}
512 
513 	enum State : ubyte { empty, value, error }
514 
515 	Storage storage;
516 	State state = State.empty;
517 	static if (!isCopyConstructorEnabled!Hook) bool checked = false;
518 
519 	void setState(MT, Flag!"force" force = No.force)()
520 	{
521 		State s;
522 		static if (Types.length == 1 && is(T == void)) s = State.error;
523 		else static if (Types.length == 1 || is(MT == T)) s = State.value;
524 		else s = State.error;
525 
526 		static if (!force)
527 		{
528 			//TODO: change with Hook?
529 			assert(state == State.empty || state == s, "Can't change meaning of already set Expected type");
530 		}
531 
532 		state = s;
533 	}
534 }
535 
536 /// Template to determine if provided Hook enables default constructor for $(LREF Expected)
537 template isDefaultConstructorEnabled(Hook)
538 {
539 	static if (__traits(hasMember, Hook, "enableDefaultConstructor"))
540 	{
541 		static assert(
542 			is(typeof(__traits(getMember, Hook, "enableDefaultConstructor")) : bool),
543 			"Hook's enableDefaultConstructor is expected to be of type bool"
544 		);
545 		static if (__traits(getMember, Hook, "enableDefaultConstructor")) enum isDefaultConstructorEnabled = true;
546 		else enum isDefaultConstructorEnabled = false;
547 	}
548 	else enum isDefaultConstructorEnabled = false;
549 }
550 
551 ///
552 unittest
553 {
554 	struct Foo {}
555 	struct Bar { static immutable bool enableDefaultConstructor = true; }
556 	static assert(!isDefaultConstructorEnabled!Foo);
557 	static assert(isDefaultConstructorEnabled!Bar);
558 }
559 
560 /++ Template to determine if hook provides function called on empty value.
561 +/
562 template hasOnAccessEmptyValue(Hook, E)
563 {
564 	static if (__traits(hasMember, Hook, "onAccessEmptyValue"))
565 	{
566 		static assert(
567 			is(typeof(__traits(getMember, Hook, "onAccessEmptyValue")(E.init))),
568 			"Hook's onAccessEmptyValue is expected to be callable with error value type"
569 		);
570 		enum hasOnAccessEmptyValue = true;
571 	}
572 	else enum hasOnAccessEmptyValue = false;
573 }
574 
575 ///
576 unittest
577 {
578 	struct Foo {}
579 	struct Bar { static void onAccessEmptyValue(E)(E err) {} }
580 	static assert(!hasOnAccessEmptyValue!(Foo, string));
581 	static assert(hasOnAccessEmptyValue!(Bar, string));
582 }
583 
584 /++ Template to determine if hook provides function called on empty error.
585 +/
586 template hasOnAccessEmptyError(Hook)
587 {
588 	static if (__traits(hasMember, Hook, "onAccessEmptyError"))
589 	{
590 		static assert(
591 			is(typeof(__traits(getMember, Hook, "onAccessEmptyError")())),
592 			"Hook's onAccessEmptyValue is expected to be callable with no arguments"
593 		);
594 		enum hasOnAccessEmptyError = true;
595 	}
596 	else enum hasOnAccessEmptyError = false;
597 }
598 
599 ///
600 unittest
601 {
602 	struct Foo {}
603 	struct Bar { static void onAccessEmptyError() {} }
604 	static assert(!hasOnAccessEmptyError!Foo);
605 	static assert(hasOnAccessEmptyError!Bar);
606 }
607 
608 /++ Template to determine if hook enables or disables copy constructor.
609 
610 	It is enabled by default.
611 
612 	See $(LREF hasOnUnchecked) handler, which can be used in combination with disabled
613 	copy constructor to enforce result check.
614 
615 	$(WARNING If copy constructor is disabled, it severely limits function chaining
616 	as $(LREF Expected) needs to be passed as rvalue in that case.)
617 +/
618 template isCopyConstructorEnabled(Hook)
619 {
620 	static if (__traits(hasMember, Hook, "enableCopyConstructor"))
621 	{
622 		static assert(
623 			is(typeof(__traits(getMember, Hook, "enableCopyConstructor")) : bool),
624 			"Hook's enableCopyConstructor is expected to be of type bool"
625 		);
626 		static if (__traits(getMember, Hook, "enableCopyConstructor")) enum isCopyConstructorEnabled = true;
627 		else enum isCopyConstructorEnabled = false;
628 	}
629 	else enum isCopyConstructorEnabled = true;
630 }
631 
632 ///
633 unittest
634 {
635 	struct Foo {}
636 	struct Bar { static immutable bool enableCopyConstructor = false; }
637 	static assert(isCopyConstructorEnabled!Foo);
638 	static assert(!isCopyConstructorEnabled!Bar);
639 }
640 
641 // just a helper to determine check behavior
642 private template isChecked(Hook)
643 {
644 	enum isChecked = !isCopyConstructorEnabled!Hook;
645 }
646 
647 /++ Template to determine if hook provides custom handler for case
648 	when the $(LREF Expected) result is not checked.
649 
650 	For this to work it currently also has to pass $(LREF isCopyConstructorEnabled)
651 	as this is implemented by simple flag controled on $(LREF Expected) destructor.
652 +/
653 template hasOnUnchecked(Hook)
654 {
655 	static if (__traits(hasMember, Hook, "onUnchecked"))
656 	{
657 		static assert(
658 			is(typeof(__traits(getMember, Hook, "onUnchecked")())),
659 			"Hook's onUnchecked is expected to be callable with no arguments"
660 		);
661 		static assert(
662 			!isCopyConstructorEnabled!Hook,
663 			"For unchecked check to work, it is currently needed to also disable copy constructor"
664 		);
665 		enum hasOnUnchecked = true;
666 	}
667 	else enum hasOnUnchecked = false;
668 }
669 
670 ///
671 @system unittest
672 {
673 	struct Foo {}
674 	struct Bar { static void onUnchecked() { throw new Exception("result unchecked"); } }
675 	struct Hook {
676 		static immutable bool enableCopyConstructor = false;
677 		static void onUnchecked() @safe { throw new Exception("result unchecked"); }
678 	}
679 
680 	// template checks
681 	static assert(!hasOnUnchecked!Foo);
682 	static assert(!__traits(compiles, hasOnUnchecked!Bar)); // missing disabled constructor
683 	static assert(hasOnUnchecked!Hook);
684 
685 	// copy constructor
686 	auto exp = expected!(string, Hook)(42);
687 	auto exp2 = unexpected!(int, Hook)("foo");
688 	static assert(!__traits(compiles, exp.andThen(expected!(string, Hook)(42)))); // disabled cc
689 	assert(exp.andThen(exp2).error == "foo"); // passed by ref so no this(this) called
690 
691 	// check for checked result
692 	assertThrown({ expected!(string, Hook)(42); }());
693 	assertThrown({ unexpected!(void, Hook)("foo"); }());
694 }
695 
696 /++ Default hook implementation for $(LREF Expected)
697 +/
698 struct Abort
699 {
700 static:
701 	/++ Default constructor for $(LREF Expected) is disabled
702 		Same with the `opAssign`, so $(LREF Expected) can be only constructed
703 		once and not modified afterwards.
704 	+/
705 	immutable bool enableDefaultConstructor = false;
706 
707 	/// Handler for case when empty value is accessed
708 	void onAccessEmptyValue(E)(E err) nothrow @nogc
709 	{
710 		assert(0, "Can't access value of unexpected");
711 	}
712 
713 	/// Handler for case when empty error is accessed
714 	void onAccessEmptyError() nothrow @nogc
715 	{
716 		assert(0, "Can't access error on expected value");
717 	}
718 }
719 
720 @system unittest
721 {
722 	static assert(!isDefaultConstructorEnabled!Abort);
723 	static assert(hasOnAccessEmptyValue!(Abort, string));
724 	static assert(hasOnAccessEmptyValue!(Abort, int));
725 	static assert(hasOnAccessEmptyError!Abort);
726 
727 	assertThrown!Throwable(expected(42).error);
728 	assertThrown!Throwable(unexpected!int("foo").value);
729 }
730 
731 /++ Hook implementation that throws exceptions instead of default assert behavior.
732 +/
733 struct Throw
734 {
735 static:
736 
737 	/++ Default constructor for $(LREF Expected) is disabled
738 		Same with the `opAssign`, so $(LREF Expected) can be only constructed
739 		once and not modified afterwards.
740 	+/
741 	immutable bool enableDefaultConstructor = false;
742 
743 	/++ Handler for case when empty value is accessed
744 
745 		Throws:
746 			If `E` inherits from `Throwable`, the error value is thrown.
747 			Otherwise, an [Unexpected] instance containing the error value is
748 			thrown.
749 	+/
750 	void onAccessEmptyValue(E)(E err)
751 	{
752 		static if(is(E : Throwable)) throw err;
753 		else throw new Unexpected!E(err);
754 	}
755 
756 	/// Handler for case when empty error is accessed
757 	void onAccessEmptyError()
758 	{
759 		throw new Unexpected!string("Can't access error on expected value");
760 	}
761 }
762 
763 unittest
764 {
765 	static assert(!isDefaultConstructorEnabled!Throw);
766 	static assert(hasOnAccessEmptyValue!(Throw, string));
767 	static assert(hasOnAccessEmptyValue!(Throw, int));
768 	static assert(hasOnAccessEmptyError!Throw);
769 
770 	assertThrown!(Unexpected!string)(expected!(string, Throw)(42).error);
771 	assertThrown!(Unexpected!string)(unexpected!(int, Throw)("foo").value);
772 }
773 
774 /++ An exception that represents an error value.
775 
776 	This is used by $(LREF Throw) hook when undefined value or error is
777 	accessed on $(LREF Expected)
778 +/
779 class Unexpected(T) : Exception
780 {
781 	T error; /// error value
782 
783 	/// Constructs an `Unexpected` exception from an error value.
784 	pure @safe @nogc nothrow
785 	this(T value, string file = __FILE__, size_t line = __LINE__)
786 	{
787 		super("Unexpected error", file, line);
788 		this.error = error;
789 	}
790 }
791 
792 /++
793 	Creates an $(LREF Expected) object from an expected value, with type inference.
794 +/
795 Expected!(T, E, Hook) expected(E = string, Hook = Abort, T)(T value)
796 {
797 	return Expected!(T, E, Hook)(value);
798 }
799 
800 /// ditto
801 Expected!(void, E, Hook) expected(E = string, Hook = Abort)()
802 {
803 	return Expected!(void, E, Hook)();
804 }
805 
806 // expected
807 unittest
808 {
809 	// void
810 	{
811 		auto res = expected();
812 		static assert(is(typeof(res) == Expected!(void, string)));
813 		assert(res);
814 	}
815 
816 	// int
817 	{
818 		auto res = expected(42);
819 		static assert(is(typeof(res) == Expected!(int, string)));
820 		assert(res);
821 		assert(res.value == 42);
822 	}
823 
824 	// string
825 	{
826 		auto res = expected("42");
827 		static assert(is(typeof(res) == Expected!(string, string)));
828 		assert(res);
829 		assert(res.value == "42");
830 	}
831 
832 	// other error type
833 	{
834 		auto res = expected!bool(42);
835 		static assert(is(typeof(res) == Expected!(int, bool)));
836 		assert(res);
837 		assert(res.value == 42);
838 	}
839 }
840 
841 /++ Constructs $(LREF Expected) from the result of the provided function.
842 
843 	If the function is `nothrow`, it just returns it's result using $(LREF Expected).
844 
845 	If not, then it uses `try catch` block and constructs $(LREF Expected) with value or error.
846 +/
847 template expected(alias fun, Hook = Abort)
848 {
849 	auto expected(Args...)(auto ref Args args) if (is(typeof(fun(args))))
850 	{
851 		import std.traits : hasFunctionAttributes;
852 
853 		alias T = typeof(fun(args));
854 		static if (is(hasFunctionAttributes!(fun, "nothrow"))) return expected!Exception(fun(args));
855 		else
856 		{
857 			try return Expected!(T, Exception)(fun(args));
858 			catch (Exception ex) return unexpected!T(ex);
859 		}
860 	}
861 }
862 
863 ///
864 unittest
865 {
866 	auto fn(int v) { if (v == 42) throw new Exception("don't panic"); return v; }
867 
868 	assert(expected!fn(1) == 1);
869 	assert(expected!fn(42).error.msg == "don't panic");
870 }
871 
872 /++
873 	Creates an $(LREF Expected) object from an error value, with type inference.
874 +/
875 Expected!(T, E, Hook) unexpected(T = void, Hook = Abort, E)(E err)
876 {
877 	static if (Expected!(T, E, Hook).Types.length == 1 && !is(T == void))
878 		return Expected!(T, E, Hook)(err, false);
879 	else return Expected!(T, E, Hook)(err);
880 }
881 
882 //unexpected
883 unittest
884 {
885 	// implicit void value type
886 	{
887 		auto res = unexpected("foo");
888 		static assert(is(typeof(res) == Expected!(void, string)));
889 		assert(!res);
890 		assert(res.error == "foo");
891 	}
892 
893 	// bool
894 	{
895 		auto res = unexpected!int("42");
896 		static assert(is(typeof(res) == Expected!(int, string)));
897 		assert(!res);
898 		assert(res.error == "42");
899 	}
900 
901 	// other error type
902 	{
903 		auto res = unexpected!bool(42);
904 		static assert(is(typeof(res) == Expected!(bool, int)));
905 		assert(!res);
906 		assert(res.error == 42);
907 	}
908 }
909 
910 /++
911 	Returns the error contained within the $(LREF Expected) _and then_ another value if there's no error.
912 	This function can be used for control flow based on $(LREF Expected) values.
913 
914 	Params:
915 		exp = The $(LREF Expected) to call andThen on
916 		value = The value to return if there isn't an error
917 		pred = The predicate to call if the there isn't an error
918 +/
919 auto ref EX andThen(EX)(auto ref EX exp, auto ref EX value)
920 	if (is(EX : Expected!(T, E, H), T, E, H))
921 {
922 	return exp.hasError ? exp : value;
923 }
924 
925 /// ditto
926 auto ref EX andThen(alias pred, EX)(auto ref EX exp)
927 	if (
928 		is(EX : Expected!(T, E, H), T, E, H)
929 		&& is(typeof(pred()) : EX)
930 	)
931 {
932 	return exp.hasError ? exp : pred();
933 }
934 
935 ///
936 unittest
937 {
938 	assert(expected(42).andThen(expected(1)) == 1);
939 	assert(expected(42).andThen!(() => expected(0)) == 0);
940 	assert(expected(42).andThen(unexpected!int("foo")).error == "foo");
941 	assert(expected(42).andThen!(() => unexpected!int("foo")).error == "foo");
942 	assert(unexpected!int("foo").andThen(expected(42)).error == "foo");
943 	assert(unexpected!int("foo").andThen!(() => expected(42)).error == "foo");
944 	assert(unexpected!int("foo").andThen(unexpected!int("bar")).error == "foo");
945 	assert(unexpected!int("foo").andThen!(() => unexpected!int("bar")).error == "foo");
946 
947 	// with void value
948 	assert(expected().andThen!(() => expected()));
949 	assert(expected().andThen!(() => unexpected("foo")).error == "foo");
950 	assert(unexpected("foo").andThen!(() => expected()).error == "foo");
951 }
952 
953 /++
954 	Returns the value contained within the $(LREF Expected) _or else_ another value if there's an error.
955 	This function can be used for control flow based on $(LREF Expected) values.
956 
957 	Params:
958 		exp = The $(LREF Expected) to call orElse on
959 		value = The value to return if there is an error
960 		pred = The predicate to call if the there is an error
961 +/
962 U orElse(EX, U)(auto ref EX exp, lazy U value)
963 	if (is(EX : Expected!(T, E, H), T, E, H) && is(U : T))
964 {
965 	return exp.orElse!value;
966 }
967 
968 /// ditto
969 auto ref orElse(alias pred, EX)(auto ref EX exp)
970 	if (is(EX : Expected!(T, E, H), T, E, H) && is(typeof(pred()) : T))
971 {
972 	return exp.hasError ? pred() : exp.value;
973 }
974 
975 /// ditto
976 auto ref orElse(alias pred, EX)(auto ref EX exp)
977 	if (
978 		is(EX : Expected!(T, E, H), T, E, H)
979 		&& is(typeof(pred()) : Expected!(T, E, H))
980 	)
981 {
982 	return exp.hasError ? pred() : exp;
983 }
984 
985 ///
986 unittest
987 {
988 	assert(expected(42).orElse(0) == 42);
989 	assert(expected(42).orElse!(() => 0) == 42);
990 	assert(unexpected!int("foo").orElse(0) == 0);
991 	assert(unexpected!int("foo").orElse!(() => 0) == 0);
992 	assert(expected(42).orElse!(() => expected(0)) == 42);
993 	assert(unexpected!int("foo").orElse!(() => expected(42)) == 42);
994 	assert(unexpected!int("foo").orElse!(() => unexpected!int("bar")).error == "bar");
995 
996 	// with void value
997 	assert(expected().orElse!(() => unexpected("foo")));
998 	assert(unexpected("foo").orElse!(() => expected()));
999 	assert(unexpected("foo").orElse!(() => unexpected("bar")).error == "bar");
1000 }
1001 
1002 /++
1003 	Applies a function to the expected value in an $(LREF Expected) object.
1004 
1005 	If no expected value is present, the original error value is passed through
1006 	unchanged, and the function is not called.
1007 
1008 	Params:
1009 		op = function called to map $(LREF Expected) value
1010 		hook = use another hook for mapped $(LREF Expected)
1011 
1012 	Returns:
1013 		A new $(LREF Expected) object containing the result.
1014 +/
1015 template map(alias op, Hook = Abort)
1016 {
1017 	/++
1018 		The actual `map` function.
1019 
1020 		Params:
1021 			self = an [Expected] object
1022 	+/
1023 	auto map(T, E, H)(auto ref Expected!(T, E, H) self)
1024 		if ((is(T == void) && is(typeof(op()))) || (!is(T == void) && is(typeof(op(self.value)))))
1025 	{
1026 		static if (is(T == void)) alias U = typeof(op());
1027 		else alias U = typeof(op(self.value));
1028 
1029 		if (self.hasError) return unexpected!(U, Hook)(self.error);
1030 		else
1031 		{
1032 			static if (is(T == void)) return expected!(E, Hook)(op());
1033 			else return expected!(E, Hook)(op(self.value));
1034 		}
1035 	}
1036 }
1037 
1038 ///
1039 unittest
1040 {
1041 	{
1042 		assert(expected(42).map!(a => a/2).value == 21);
1043 		assert(expected().map!(() => 42).value == 42);
1044 		assert(unexpected!int("foo").map!(a => 42).error == "foo");
1045 		assert(unexpected("foo").map!(() => 42).error == "foo");
1046 	}
1047 
1048 	// remap hook
1049 	{
1050 		static struct Hook {}
1051 		auto res = expected(42).map!(a => a/2, Hook);
1052 		assert(res == 21);
1053 		static assert(is(typeof(res) == Expected!(int, string, Hook)));
1054 	}
1055 }
1056 
1057 /++
1058 	Applies a function to the expected error in an $(LREF Expected) object.
1059 
1060 	If no error is present, the original value is passed through
1061 	unchanged, and the function is not called.
1062 
1063 	Params:
1064 		op = function called to map $(LREF Expected) error
1065 		hook = use another hook for mapped $(LREF Expected)
1066 
1067 	Returns:
1068 		A new $(LREF Expected) object containing the result.
1069 +/
1070 template mapError(alias op, Hook = Abort)
1071 {
1072 	/++
1073 		The actual `mapError` function.
1074 
1075 		Params:
1076 			self = an [Expected] object
1077 	+/
1078 	auto mapError(T, E, H)(auto ref Expected!(T, E, H) self)
1079 		if (is(typeof(op(self.error))))
1080 	{
1081 		alias U = typeof(op(self.error));
1082 
1083 		static if (!is(T == void))
1084 		{
1085 			if (self.hasValue) return expected!(U, Hook)(self.value);
1086 		}
1087 		return unexpected!(T, Hook)(op(self.error));
1088 	}
1089 }
1090 
1091 ///
1092 unittest
1093 {
1094 	{
1095 		assert(expected(42).mapError!(e => e).value == 42);
1096 		assert(unexpected("foo").mapError!(e => 42).error == 42);
1097 		assert(unexpected("foo").mapError!(e => new Exception(e)).error.msg == "foo");
1098 	}
1099 
1100 	// remap hook
1101 	{
1102 		static struct Hook {}
1103 		auto res = expected(42).mapError!(e => e, Hook);
1104 		assert(res == 42);
1105 		static assert(is(typeof(res) == Expected!(int, string, Hook)));
1106 
1107 		auto res2 = unexpected!int("foo").mapError!(e => "bar", Hook);
1108 		assert(res2.error == "bar");
1109 		static assert(is(typeof(res2) == Expected!(int, string, Hook)));
1110 	}
1111 }
1112 
1113 /++
1114 	Maps a `Expected<T, E>` to `U` by applying a function to a contained value, or a fallback function to a contained error value.
1115 
1116 	Both functions has to be of the same return type.
1117 
1118 	This function can be used to unpack a successful result while handling an error.
1119 
1120 	Params:
1121 		valueOp = function called to map $(LREF Expected) value
1122 		errorOp = function called to map $(LREF Expected) error
1123 		hook = use another hook for mapped $(LREF Expected)
1124 
1125 	Returns:
1126 		A new $(LREF Expected) object containing the result.
1127 +/
1128 template mapOrElse(alias valueOp, alias errorOp)
1129 {
1130 	/++
1131 		The actual `mapOrElse` function.
1132 
1133 		Params:
1134 			self = an [Expected] object
1135 	+/
1136 	auto mapOrElse(T, E, H)(auto ref Expected!(T, E, H) self)
1137 		if (
1138 			is(typeof(errorOp(self.error))) &&
1139 			(
1140 				(is(T == void) && is(typeof(valueOp()) == typeof(errorOp(self.error)))) ||
1141 				(!is(T == void) && is(typeof(valueOp(self.value)) == typeof(errorOp(self.error))))
1142 			)
1143 		)
1144 	{
1145 		alias U = typeof(errorOp(self.error));
1146 
1147 		if (self.hasError) return errorOp(self.error);
1148 		else
1149 		{
1150 			static if (is(T == void)) return valueOp();
1151 			else return valueOp(self.value);
1152 		}
1153 	}
1154 }
1155 
1156 ///
1157 unittest
1158 {
1159 	assert(expected(42).mapOrElse!(v => v/2, e => 0) == 21);
1160 	assert(expected().mapOrElse!(() => true, e => false));
1161 	assert(unexpected!int("foo").mapOrElse!(v => v/2, e => 42) == 42);
1162 	assert(!unexpected("foo").mapOrElse!(() => true, e => false));
1163 }
1164 
1165 // -- global tests --
1166 
1167 // Expected.init
1168 @system nothrow unittest
1169 {
1170 	struct EnableDefaultConstructor { static immutable bool enableDefaultConstructor = true; }
1171 
1172 	{
1173 		auto res = Expected!(int, string).init;
1174 		assert(!res.hasValue && !res.hasError);
1175 		assert(res);
1176 		assertThrown!Throwable(res.value);
1177 		assertThrown!Throwable(res.error);
1178 		static assert(!__traits(compiles, res = 42));
1179 	}
1180 
1181 	{
1182 		auto res = Expected!(int, string, EnableDefaultConstructor).init;
1183 		assert(!res.hasValue && !res.hasError);
1184 		assert(res);
1185 		assert(res.value == 0);
1186 		assert(res.error is null);
1187 		res = 42;
1188 		assert(res.value == 42);
1189 	}
1190 
1191 	// T == void
1192 	{
1193 		auto res = Expected!(void, string).init;
1194 		static assert(!__traits(compiles, res.hasValue));
1195 		static assert(!__traits(compiles, res.value));
1196 		static assert(!__traits(compiles, res = "foo"));
1197 		assert(!res.hasError);
1198 		assert(res);
1199 		assertThrown!Throwable(res.error);
1200 	}
1201 
1202 	// T == void
1203 	{
1204 		auto res = Expected!(void, string, EnableDefaultConstructor).init;
1205 		static assert(!__traits(compiles, res.hasValue));
1206 		static assert(!__traits(compiles, res.value));
1207 		assert(!res.hasError);
1208 		assert(res.state == Expected!(void, string, EnableDefaultConstructor).State.empty);
1209 		assert(res);
1210 		assert(res.error is null);
1211 		res = "foo";
1212 		assert(res.error == "foo");
1213 	}
1214 }
1215 
1216 // Default constructor - disabled
1217 unittest
1218 {
1219 	static assert(!__traits(compiles, Expected!(int, string)()));
1220 }
1221 
1222 // Default constructor - enabled
1223 @system nothrow unittest
1224 {
1225 	struct EnableDefaultConstructor { static immutable bool enableDefaultConstructor = true; }
1226 	{
1227 		auto res = Expected!(int, string, EnableDefaultConstructor)();
1228 		assert(!res.hasValue && !res.hasError);
1229 		assert(res);
1230 		assert(res.value == 0);
1231 		assert(res.error is null);
1232 		res = 42;
1233 		assert(res);
1234 		assert(res.value == 42);
1235 	}
1236 
1237 	{
1238 		auto res = Expected!(void, string, EnableDefaultConstructor)();
1239 		assert(!res.hasError);
1240 		assert(res);
1241 		assert(res.error is null);
1242 		res = "foo";
1243 		assert(res.hasError);
1244 		assert(!res);
1245 		assert(res.error == "foo");
1246 	}
1247 }
1248 
1249 // Default types
1250 nothrow @nogc unittest
1251 {
1252 	auto res = Expected!(int)(42);
1253 	assert(res);
1254 	assert(res.hasValue && !res.hasError);
1255 	assert(res.value == 42);
1256 	res.value = 43;
1257 	assert(res.value == 43);
1258 }
1259 
1260 // Default types with const payload
1261 nothrow @nogc unittest
1262 {
1263 	alias Exp = Expected!(const(int));
1264 	static assert(is(typeof(Exp.init.value) == const(int)));
1265 	auto res = Exp(42);
1266 	assert(res);
1267 	assert(res.hasValue && !res.hasError);
1268 	assert(res.value == 42);
1269 	static assert(!__traits(compiles, res.value = res.value));
1270 }
1271 
1272 // Default types with immutable payload
1273 unittest
1274 {
1275 	alias Exp = Expected!(immutable(int));
1276 	static assert(is(typeof(Exp.init.value) == immutable(int)));
1277 	auto res = Exp(42);
1278 	assert(res);
1279 	assert(res.hasValue && !res.hasError);
1280 	assert(res.value == 42);
1281 	static assert(!__traits(compiles, res.value = res.value));
1282 }
1283 
1284 // opAssign
1285 @system nothrow unittest
1286 {
1287 	struct EnableDefaultConstructor { static immutable bool enableDefaultConstructor = true; }
1288 	// value
1289 	{
1290 		auto res = Expected!(int, string, EnableDefaultConstructor).init;
1291 		res = 42;
1292 		assert(res);
1293 		assert(res.hasValue && !res.hasError);
1294 		assert(res.value == 42);
1295 		res = 43;
1296 		assertThrown!Throwable(res = "foo");
1297 	}
1298 
1299 	// error
1300 	{
1301 		auto res = Expected!(int, string, EnableDefaultConstructor)("42");
1302 		assert(!res.hasValue && res.hasError);
1303 		assert(res.error == "42");
1304 		res = "foo";
1305 		assert(res.error == "foo");
1306 		assertThrown!Throwable(res = 42);
1307 	}
1308 }
1309 
1310 // Same types
1311 @system nothrow unittest
1312 {
1313 	{
1314 		alias Exp = Expected!(int, int);
1315 		auto res = Exp(42);
1316 		assert(res);
1317 		assert(res.hasValue && !res.hasError);
1318 		assert(res.value == 42);
1319 		assertThrown!Throwable(res.error());
1320 	}
1321 
1322 	// const mix
1323 	{
1324 		alias Exp = Expected!(const(int), int);
1325 		auto res = Exp(const int(42));
1326 		auto val = res.value;
1327 		static assert(is(typeof(val) == const int));
1328 		assert(res);
1329 		assert(res.hasValue && !res.hasError);
1330 		assert(res.value == 42);
1331 		assertThrown!Throwable(res.error);
1332 	}
1333 
1334 	// const mix
1335 	{
1336 		alias Exp = Expected!(const(int), int);
1337 		auto res = Exp(42);
1338 		auto err = res.error;
1339 		static assert(is(typeof(err) == int));
1340 		assert(!res);
1341 		assert(!res.hasValue && res.hasError);
1342 		assert(res.error == 42);
1343 		assertThrown!Throwable(res.value);
1344 	}
1345 
1346 	// immutable mix
1347 	{
1348 		alias Exp = Expected!(immutable(int), int);
1349 		auto res = Exp(immutable int(42));
1350 		auto val = res.value;
1351 		static assert(is(typeof(val) == immutable int));
1352 		assert(res);
1353 		assert(res.hasValue && !res.hasError);
1354 		assert(res.value == 42);
1355 		assertThrown!Throwable(res.error);
1356 	}
1357 
1358 	// immutable mix
1359 	{
1360 		alias Exp = Expected!(immutable(int), int);
1361 		auto res = Exp(42);
1362 		auto err = res.error;
1363 		static assert(is(typeof(err) == int));
1364 		assert(!res);
1365 		assert(!res.hasValue && res.hasError);
1366 		assert(res.error == 42);
1367 		assertThrown!Throwable(res.value);
1368 	}
1369 
1370 	// immutable mix reverse
1371 	{
1372 		alias Exp = Expected!(int, immutable(int));
1373 		auto res = Exp(immutable int(42));
1374 		auto err = res.error;
1375 		static assert(is(typeof(err) == immutable int));
1376 		assert(!res);
1377 		assert(!res.hasValue && res.hasError);
1378 		assert(res.error == 42);
1379 		assertThrown!Throwable(res.value);
1380 	}
1381 
1382 	// immutable mix reverse
1383 	{
1384 		alias Exp = Expected!(int, immutable(int));
1385 		auto res = Exp(42);
1386 		auto val = res.value;
1387 		static assert(is(typeof(val) == int));
1388 		assert(res);
1389 		assert(res.hasValue && !res.hasError);
1390 		assert(res.value == 42);
1391 		assertThrown!Throwable(res.error);
1392 	}
1393 }
1394 
1395 // void payload
1396 nothrow @nogc unittest
1397 {
1398 	alias Exp = Expected!(void, int);
1399 	static assert (!__traits(hasMember, Exp, "hasValue"));
1400 	static assert (!__traits(hasMember, Exp, "value"));
1401 
1402 	{
1403 		auto res = Exp();
1404 		assert(res);
1405 		assert(!res.hasError);
1406 	}
1407 
1408 	{
1409 		auto res = Exp(42);
1410 		assert(!res);
1411 		assert(res.hasError);
1412 		assert(res.error == 42);
1413 	}
1414 }
1415 
1416 // opEquals
1417 unittest
1418 {
1419 	assert(expected(42) == 42);
1420 	assert(expected(42) != 43);
1421 	assert(expected("foo") == "foo");
1422 	assert(expected("foo") != "bar");
1423 	assert(expected("foo") == cast(const string)"foo");
1424 	assert(expected("foo") == cast(immutable string)"foo");
1425 	assert(expected(42) == expected(42));
1426 	assert(expected(42) != expected(43));
1427 	assert(expected(42) != unexpected!int("42"));
1428 
1429 	static assert(!__traits(compiles, unexpected("foo") == "foo"));
1430 	assert(unexpected(42) == unexpected(42));
1431 	assert(unexpected(42) != unexpected(43));
1432 	assert(unexpected("foo") == unexpected("foo"));
1433 	assert(unexpected("foo") != unexpected("bar"));
1434 }
1435 
1436 //FIXME: doesn't work - some older dmd error
1437 static if (__VERSION__ >= 2082)
1438 {
1439 	// toHash
1440 	unittest
1441 	{
1442 		assert(expected(42).hashOf == 42.hashOf);
1443 		assert(expected(42).hashOf != 43.hashOf);
1444 		assert(expected(42).hashOf == expected(42).hashOf);
1445 		assert(expected(42).hashOf != expected(43).hashOf);
1446 		assert(expected(42).hashOf == expected!bool(42).hashOf);
1447 		assert(expected(42).hashOf != unexpected("foo").hashOf);
1448 		assert(unexpected("foo").hashOf == unexpected("foo").hashOf);
1449 	}
1450 }
1451 
1452 // range interface
1453 unittest
1454 {
1455 	{
1456 		auto r = expected(42);
1457 		assert(!r.empty);
1458 		assert(r.front == 42);
1459 		r.popFront();
1460 		assert(r.empty);
1461 	}
1462 
1463 	{
1464 		auto r = unexpected!int("foo");
1465 		assert(r.empty);
1466 	}
1467 
1468 	{
1469 		auto r = unexpected("foo");
1470 		static assert(!__traits(compiles, r.empty));
1471 		static assert(!__traits(compiles, r.front));
1472 		static assert(!__traits(compiles, r.popFront));
1473 	}
1474 }