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 features
14 
15 $(LIST
16     * lightweight, no other external dependencies
17     * works with `pure`, `@safe`, `@nogc`, `nothrow`, and `immutable`
18     * provides methods: `ok`, `err`, `consume`, `expect`, `expectErr`, `andThen`, `orElse`, `map`, `mapError`, `mapOrElse`
19     * type inference for ease of use with `ok` and `err`
20     * allows to use same types for `T` and `E`
21     * allows to define $(LREF Expected) without value (`void` for `T`) - can be disabled with custom `Hook`
22     * provides facility to change the $(LREF Expected) behavior by custom `Hook` implementation using the Design by introspection paradigm.
23     * can enforce result check (with a cost)
24     * can behave like a normal `Exception` handled code by changing the used `Hook` implementation
25     * range interface
26 )
27 
28 ## Description
29 
30 Actual $(LREF Expected) type is defined as $(D Expected!(T, E, Hook)), where:
31 
32 $(LIST
33     * `T` defines type of the success value
34     * `E` defines type of the error
35     * `Hook` defines behavior of the $(LREF Expected)
36 )
37 
38 Default type for error is `string`, i.e. `Expected!int` is the same as `Expected!(int, string)`.
39 
40 $(LREF Abort) is used as a default hook.
41 
42 ### Hooks
43 
44 $(LREF Expected) has customizable behavior with the help of a third type parameter,
45 `Hook`. Depending on what methods `Hook` defines, core operations on the
46 $(LREF Expected) may be verified or completely redefined.
47 If `Hook` defines no method at all and carries no state, there is no change in
48 default behavior.
49 
50 This module provides a few predefined hooks (below) that add useful behavior to
51 $(LREF Expected):
52 
53 $(BOOKTABLE ,
54     $(TR $(TD $(LREF Abort)) $(TD
55         Fails every incorrect operation with a call to `assert(0)`.
56         It is the default third parameter, i.e. $(D Expected!short) is the same as
57         $(D Expected!(short, string, Abort)).
58     ))
59     $(TR $(TD $(LREF Throw)) $(TD
60         Fails every incorrect operation by throwing an exception.
61     ))
62     $(TR $(TD $(LREF AsException)) $(TD
63         With this hook implementation $(LREF Expected) behaves just like regular
64         $(D Exception) handled code.
65 
66         That means when function returns $(LREF expected) value, it returns instance
67         of $(LREF Expected) with a success value.
68         But when it tries to return error, $(D Exception) is thrown right away,
69         i.e. $(LREF Expected) fails in constructor.
70     ))
71     $(TR $(TD $(LREF RCAbort)) $(TD
72         Similar to $(LREF Abort) hook but uses reference counted payload instead
73         which enables checking if the caller properly checked result of the
74         $(LREF Expected).
75     ))
76 )
77 
78 The hook's members are looked up statically in a Design by Introspection manner
79 and are all optional. The table below illustrates the members that a hook type
80 may define and their influence over the behavior of the `Checked` type using it.
81 In the table, `hook` is an alias for `Hook` if the type `Hook` does not
82 introduce any state, or an object of type `Hook` otherwise.
83 
84 $(TABLE_ROWS
85     * + Hook member
86       + Semantics in Expected!(T, E, Hook)
87     * - `enableDefaultConstructor`
88       - If defined, $(LREF Expected) would have enabled or disabled default constructor
89         based on it's `bool` value. Default constructor is disabled by default.
90         `opAssign` for value and error types is generated if default constructor is enabled.
91     * - `enableCopyConstructor`
92       - If defined, $(LREF Expected) would have enabled or disabled copy constructor based
93         on it's `bool` value. It is enabled by default. When disabled, it enables automatic
94         check if the result was checked either for value or error.
95         When not checked it calls $(D hook.onUnchecked) if provided.
96 
97         $(NOTE WARNING: As currently it's not possible to change internal state of `const`
98         or `immutable` object, automatic checking would't work on these. Hopefully with
99         `__mutable` proposal..)
100     * - `enableRefCountedPayload`
101       - Set $(LREF Expected) instances to use reference counted payload storage. It's usefull
102         when combined with `onUnchecked` to forcibly check that the result was checked for value
103         or error.
104     * - `enableVoidValue`
105       - Defines if $(LREF Expected) supports `void` values. It's enabled by default so this
106         hook can be used to disable it.
107     * - `onAccessEmptyValue`
108       - If value is accessed on unitialized $(LREF Expected) or $(LREF Expected) with error
109         value, $(D hook.onAccessEmptyValue!E(err)) is called. If hook doesn't implement the
110         handler, `T.init` is returned.
111     * - `onAccessEmptyError`
112       - If error is accessed on unitialized $(LREF Expected) or $(LREF Expected) with value,
113         $(D hook.onAccessEmptyError()) is called. If hook doesn't implement the handler,
114         `E.init` is returned.
115     * - `onUnchecked`
116       - If the result of $(LREF Expected) isn't checked, $(D hook.onUnchecked()) is called to
117         handle the error. If hook doesn't implement the handler, assert is thrown.
118         $(NOTE Note that `hook.enableCopyConstructor` must be `false` or `hook.enableRefCountedPayload`
119         must be `true` for checks to work.)
120     * - `onValueSet`
121       - $(D hook.onValueSet!T(val)) function is called when success value is being set to
122         $(LREF Expected). It can be used for loging purposes, etc.
123     * - `onErrorSet`
124       - $(D hook.onErrorSet!E(err)) function is called when error value is being set to
125         $(LREF Expected). This hook function is used by $(LREF AsException) hook implementation
126         to change `Expected` idiom to normal `Exception` handling behavior.
127 )
128 
129 License: BSL-1.0
130 Author: Tomáš Chaloupka
131 +/
132 
133 //TODO: collect errno function call - see https://dlang.org/phobos/std_exception.html#ErrnoException
134 
135 module expected;
136 
137 /// $(H3 Basic usage)
138 @("Basic usage example")
139 @safe unittest
140 {
141     auto foo(int i) {
142         if (i == 0) return err!int("oops");
143         return ok(42 / i);
144     }
145 
146     version (D_Exceptions)
147     {
148         auto bar(int i) {
149             if (i == 0) throw new Exception("err");
150             return i-1;
151         }
152     }
153 
154     // basic checks
155     assert(foo(2));
156     assert(foo(2).hasValue);
157     assert(!foo(2).hasError);
158     assert(foo(2).value == 21);
159 
160     assert(!foo(0));
161     assert(!foo(0).hasValue);
162     assert(foo(0).hasError);
163     assert(foo(0).error == "oops");
164 
165     // void result
166     assert(ok()); // no error -> success
167     assert(!ok().hasError);
168     // assert(err("foo").hasValue); // doesn't have hasValue and value properties
169 
170     version (D_Exceptions)
171     {
172         // expected from throwing function
173         assert(consume!bar(1) == 0);
174         assert(consume!bar(0).error.msg == "err");
175     }
176 
177     // orElse
178     assert(foo(2).orElse!(() => 0) == 21);
179     assert(foo(0).orElse(100) == 100);
180 
181     // andThen
182     assert(foo(2).andThen(foo(6)) == 7);
183     assert(foo(0).andThen(foo(6)).error == "oops");
184 
185     // map
186     assert(foo(2).map!(a => a*2).map!(a => a - 2) == 40);
187     assert(foo(0).map!(a => a*2).map!(a => a - 2).error == "oops");
188 
189     // mapError
190     assert(foo(0).mapError!(e => "OOPS").error == "OOPS");
191     assert(foo(2).mapError!(e => "OOPS") == 21);
192 
193     // mapOrElse
194     assert(foo(2).mapOrElse!(v => v*2, e => 0) == 42);
195     assert(foo(0).mapOrElse!(v => v*2, e => 0) == 0);
196 }
197 
198 version (D_Exceptions)
199 {
200     /// $(H3 Advanced usage - behavior modification)
201     @("Advanced usage example")
202     unittest
203     {
204         import exp = expected;
205 
206         // define our Expected type using Exception as Error values
207         // and Throw hook, which throws when empty value or error is accessed
208         template Expected(T)
209         {
210             alias Expected = exp.Expected!(T, Exception, Throw);
211         }
212 
213         // create wrappers for simplified usage of our Expected
214         auto ok(T)(T val) { return exp.ok!(Exception, Throw)(val); }
215         auto err(T)(Exception err) { return exp.err!(T, Throw)(err); }
216 
217         // use it as normal
218         assert(ok(42) == 42);
219         assert(err!int(new Exception("foo")).orElse(0) == 0);
220         assertThrown(ok(42).error);
221         assertThrown(err!int(new Exception("bar")).value);
222     }
223 }
224 
225 version (unittest) {
226     import std.algorithm : reverse;
227     import std.exception : assertThrown, collectExceptionMsg;
228 }
229 
230 @safe:
231 
232 /++
233     `Expected!(T, E)` is a type that represents either success or failure.
234 
235     Type `T` is used for success value.
236     If `T` is `void`, then $(LREF Expected) can only hold error value and is considered a success when there is no error value.
237 
238     Type `E` is used for error value.
239     The default type for the error value is `string`.
240 
241     Default behavior of $(LREF Expected) can be modified by the `Hook` template parameter.
242 
243     Params:
244         T    = represents type of the expected value
245         E    = represents type of the error value.
246         Hook = defines the $(LREF Expected) type behavior
247 +/
248 struct Expected(T, E = string, Hook = Abort)
249     if (!is(E == void) && (isVoidValueEnabled!Hook || !is(T == void)))
250 {
251     import std.algorithm : move;
252     import std.meta : AliasSeq, Filter, NoDuplicates;
253     import std.traits: isAssignable, isCopyable, hasIndirections, Unqual;
254 
255     private template noVoid(T) { enum noVoid = !is(T == void); } // Erase removes qualifiers
256     private alias Types = NoDuplicates!(Filter!(noVoid, AliasSeq!(T, E)));
257 
258     private template isMoveable(T) { enum isMoveable = __traits(compiles, (T a) { return a.move; }); }
259 
260     static foreach (i, CT; Types)
261     {
262         /++
263             Constructs an $(LREF Expected) with value or error based on the tye of the provided.
264 
265             In case when `T == E`, it constructs $(LREF Expected) with value.
266 
267             In case when `T == void`, it constructs $(LREF Expected) with error value.
268 
269             Default constructor (if enabled) initializes $(LREF Expected) to `T.init` value.
270             If `T == void`, it initializes $(LREF Expected) with no error.
271         +/
272         this()(auto ref CT val)
273         {
274             static if (isRefCountedPayloadEnabled!Hook)
275             {
276                 static if (isCopyable!CT) initialize(val);
277                 else static if (isMoveable!CT) initialize(move(val));
278                 else static assert(0, "Can't consume " ~ CT.stringof);
279             }
280             else
281             {
282                 static if (isCopyable!CT) storage = Payload(val);
283                 else static if (isMoveable!CT) storage = Payload(move(val));
284                 else static assert(0, "Can't consume " ~ CT.stringof);
285             }
286             setState!CT();
287 
288             static if (hasOnValueSet!(Hook, CT)) { if (state == State.value) __traits(getMember, Hook, "onValueSet")(val); }
289             static if (hasOnErrorSet!(Hook, CT)) { if (state == State.error) __traits(getMember, Hook, "onErrorSet")(val); }
290         }
291 
292         // static if (isCopyable!CT)
293         // {
294         //     / ditto
295         //     this()(auto ref const(CT) val) const
296         //     {
297         //         storage = const(Payload)(val);
298         //         setState!CT();
299 
300         //         static if (hasOnValueSet!(Hook, CT)) { if (state == State.value) __traits(getMember, Hook, "onValueSet")(val); }
301         //         static if (hasOnErrorSet!(Hook, CT)) { if (state == State.error) __traits(getMember, Hook, "onErrorSet")(val); }
302         //     }
303 
304         //     /// ditto
305         //     this()(auto ref immutable(CT) val) immutable
306         //     {
307         //         storage = immutable(Payload)(val);
308         //         setState!CT();
309 
310         //         static if (hasOnValueSet!(Hook, CT)) { if (state == State.value) __traits(getMember, Hook, "onValueSet")(val); }
311         //         static if (hasOnErrorSet!(Hook, CT)) { if (state == State.error) __traits(getMember, Hook, "onErrorSet")(val); }
312         //     }
313         // }
314         // else
315         // {
316         //     @disable this(const(CT) val) const;
317         //     @disable this(immutable(CT) val) immutable;
318         // }
319     }
320 
321     // generate constructor with flag to determine type of value
322     static if (Types.length == 1 && !is(T == void))
323     {
324         /++ Constructs an $(LREF Expected) with value or error based on the provided flag.
325             This constructor is available only for cases when value and error has the same type,
326             so we can still construct $(LREF Expected) with value or error.
327 
328             Params:
329                 val     = Value to set as value or error
330                 success = If `true`, $(LREF Expected) with value is created, $(LREF Expected) with error otherwise.
331         +/
332         this()(auto ref E val, bool success)
333         {
334             static if (isRefCountedPayloadEnabled!Hook)
335             {
336                 static if (isCopyable!E) initialize(val);
337                 else static if (isMoveable!E) initialize(move(val));
338                 else static assert(0, "Can't consume " ~ E.stringof);
339             }
340             else
341             {
342                 static if (isCopyable!E) storage = Payload(val);
343                 else static if (isMoveable!E) storage = Payload(val);
344                 else static assert(0, "Can't consume " ~ E.stringof);
345             }
346             setState!E(success ? State.value : State.error);
347 
348             static if (hasOnValueSet!(Hook, E)) { if (state == State.value) __traits(getMember, Hook, "onValueSet")(val); }
349             static if (hasOnErrorSet!(Hook, E)) { if (state == State.error) __traits(getMember, Hook, "onErrorSet")(val); }
350         }
351 
352         // static if (isCopyable!E)
353         // {
354         //     /// ditto
355         //     this()(auto ref const(E) val, bool success) const
356         //     {
357         //         storage = const(Payload)(val);
358         //         setState!E(success ? State.value : State.error);
359 
360         //         static if (hasOnValueSet!(Hook, E)) { if (state == State.value) __traits(getMember, Hook, "onValueSet")(val); }
361         //         static if (hasOnErrorSet!(Hook, E)) { if (state == State.error) __traits(getMember, Hook, "onErrorSet")(val); }
362         //     }
363 
364         //     /// ditto
365         //     this()(auto ref immutable(E) val, bool success) immutable
366         //     {
367         //         storage = immutable(Payload)(val);
368         //         setState!E(success ? State.value : State.error);
369 
370         //         static if (hasOnValueSet!(Hook, E)) { if (state == State.value) __traits(getMember, Hook, "onValueSet")(val); }
371         //         static if (hasOnErrorSet!(Hook, E)) { if (state == State.error) __traits(getMember, Hook, "onErrorSet")(val); }
372         //     }
373         // }
374         // else
375         // {
376         //     @disable this(const(E) val, bool success) const;
377         //     @disable this(immutable(E) val, bool success) immutable;
378         // }
379     }
380 
381     static if (!is(T == void) && !isDefaultConstructorEnabled!Hook) @disable this();
382 
383     static if (!isCopyConstructorEnabled!Hook) @disable this(this);
384 
385     static if (isChecked!Hook || isRefCountedPayloadEnabled!Hook)
386     {
387         ~this()
388         {
389             static void onUnchecked()
390             {
391                 static if (hasOnUnchecked!Hook) __traits(getMember, Hook, "onUnchecked")();
392                 else assert(0, "unchecked result");
393             }
394 
395             static if (isRefCountedPayloadEnabled!Hook)
396             {
397                 if (!storage) return;
398                 assert(storage.count > 0);
399 
400                 if (--storage.count) return;
401 
402                 // Done, deallocate
403                 static if (isChecked!Hook) bool ch = checked;
404                 destroy(storage.payload);
405                 static if (enableGCScan) pureGcRemoveRange(&storage.payload);
406 
407                 pureFree(storage);
408                 storage = null;
409 
410                 static if (isChecked!Hook) { if (!ch) onUnchecked(); }
411             }
412             else static if (isChecked!Hook) { if (!checked) onUnchecked(); }
413         }
414     }
415 
416     static if (isDefaultConstructorEnabled!Hook)
417     {
418         static foreach (i, CT; Types)
419         {
420             static if (isAssignable!CT)
421             {
422                 /++ Assigns a value or error to an $(LREF Expected).
423 
424                     Note: This is only allowed when default constructor is also enabled.
425                 +/
426                 void opAssign()(auto ref CT rhs)
427                 {
428                     setState!CT(); // check state asserts before change
429                     auto s = state;
430                     static if (isRefCountedPayloadEnabled!Hook)
431                     {
432                         if (!storage) initialize(rhs);
433                         else storage.payload = Payload(rhs);
434                     }
435                     else storage = Payload(rhs);
436                     setState!(CT)(s); // set previous state
437 
438                     static if (hasOnValueSet!(Hook, CT)) { if (state == State.value) __traits(getMember, Hook, "onValueSet")(val); }
439                     static if (hasOnErrorSet!(Hook, CT)) { if (state == State.error) __traits(getMember, Hook, "onErrorSet")(val); }
440                 }
441             }
442         }
443     }
444 
445     //damn these are ugly :(
446     static if (!isChecked!Hook) {
447         /++ Implicit conversion to bool.
448             Returns: `true` if there is no error set, `false` otherwise.
449         +/
450         bool opCast(T)() const if (is(T == bool)) { return !this.hasError; }
451     } else {
452         /// ditto
453         bool opCast(T)() if (is(T == bool)) { return !this.hasError; }
454     }
455 
456     static if (!is(T == void))
457     {
458         static if (!isChecked!Hook) {
459             /++ Checks whether this $(LREF Expected) object contains a specific expected value.
460 
461                 * `opEquals` for the value is available only when `T != void`.
462                 * `opEquals` for the error isn't available, use equality test for $(LREF Expected) in that case.
463             +/
464             bool opEquals()(const auto ref T rhs) const
465             {
466                 return hasValue && value == rhs;
467             }
468         } else {
469             /// ditto
470             bool opEquals()(auto ref T rhs) { return hasValue && value == rhs; }
471         }
472     }
473 
474     static if (!isChecked!Hook) {
475         /// Checks whether this $(LREF Expected) object and `rhs` contain the same expected value or error value.
476         bool opEquals()(const auto ref Expected!(T, E, Hook) rhs) const
477         {
478             if (state != rhs.state) return false;
479             static if (!is(T == void)) { if (hasValue) return value == rhs.value; }
480             return error == rhs.error;
481         }
482     } else {
483         /// ditto
484         bool opEquals()(auto ref Expected!(T, E, Hook) rhs)
485         {
486             if (state != rhs.state) return false;
487             static if (!is(T == void)) { if (hasValue) return value == rhs.value; }
488             return error == rhs.error;
489         }
490     }
491 
492     static if (!isChecked!Hook) {
493         /++ Calculates the hash value of the $(LREF Expected) in a way that iff it has a value,
494             it returns hash of the value.
495             Hash is computed using internal state and storage of the $(LREF Expected) otherwise.
496         +/
497         size_t toHash()() const nothrow
498         {
499             static if (!is(T == void)) { if (hasValue) return value.hashOf; }
500             return storage.hashOf(state);
501         }
502     } else {
503         /// ditto
504         size_t toHash()() nothrow
505         {
506             static if (!is(T == void)) { if (hasValue) return value.hashOf; }
507             return storage.hashOf(state);
508         }
509     }
510 
511     static if (!is(T == void))
512     {
513         static if (!isChecked!Hook) {
514             /// Checks if $(LREF Expected) has value
515             @property bool hasValue()() const { return state == State.value; }
516         }
517         else {
518             /// ditto
519             @property bool hasValue()()
520             {
521                 checked = true;
522                 return state == State.value;
523             }
524         }
525 
526         static if (!isChecked!Hook) {
527             /++
528                 Returns the expected value if there is one.
529 
530                 With default `Abort` hook, it asserts when there is no value.
531                 It calls hook's `onAccessEmptyValue` otherwise.
532 
533                 It returns `T.init` when hook doesn't provide `onAccessEmptyValue`.
534             +/
535             @property auto ref inout(T) value() inout
536             {
537                 if (state != State.value)
538                 {
539                     static if (hasOnAccessEmptyValue!(Hook, E))
540                         __traits(getMember, Hook, "onAccessEmptyValue")(state == State.error ? getError() : E.init);
541                     else return T.init;
542                 }
543                 return getValue();
544             }
545         } else {
546             @property auto ref T value()
547             {
548                 checked = true;
549 
550                 if (state != State.value)
551                 {
552                     static if (hasOnAccessEmptyValue!(Hook, E))
553                         __traits(getMember, Hook, "onAccessEmptyValue")(state == State.error ? getError() : E.init);
554                     else return T.init;
555                 }
556                 return getValue();
557             }
558         }
559     }
560 
561     static if (!isChecked!Hook) {
562         /// Checks if $(LREF Expected) has error
563         @property bool hasError()() const { return state == State.error; }
564     } else {
565         /// ditto
566         @property bool hasError()()
567         {
568             checked = true;
569             return state == State.error;
570         }
571     }
572 
573     static if (!isChecked!Hook) {
574         /++
575             Returns the error value. May only be called when `hasValue` returns `false`.
576 
577             If there is no error value, it calls hook's `onAccessEmptyError`.
578 
579             It returns `E.init` when hook doesn't provide `onAccessEmptyError`.
580         +/
581         @property auto ref inout(E) error() inout
582         {
583             if (state != State.error)
584             {
585                 static if (hasOnAccessEmptyError!Hook) __traits(getMember, Hook, "onAccessEmptyError")();
586                 else return E.init;
587             }
588             return getError;
589         }
590     } else {
591         @property auto ref E error()
592         {
593             checked = true;
594 
595             if (state != State.error)
596             {
597                 static if (hasOnAccessEmptyError!Hook) __traits(getMember, Hook, "onAccessEmptyError")();
598                 else return E.init;
599             }
600             return getError;
601         }
602     }
603 
604     // range interface
605     static if (!is(T == void))
606     {
607         static if (!isChecked!Hook) {
608             /++ Range interface defined by `empty`, `front`, `popFront`.
609                 Yields one value if $(LREF Expected) has value.
610 
611                 If `T == void`, range interface isn't defined.
612             +/
613             @property bool empty() const { return state != State.value; }
614 
615             /// ditto
616             @property auto ref inout(T) front() inout { return value; }
617         } else {
618             @property bool empty() { checked = true; return state != State.value; }
619 
620             /// ditto
621             @property auto ref T front() { return value; }
622         }
623 
624         /// ditto
625         void popFront() { state = State.empty; }
626     }
627 
628     private:
629 
630     //FIXME: can probably be union instead, but that doesn't work well with destructors and copy constructors/postblits
631     //and need to be handled manually - so for now we use a safer variant
632     struct Payload
633     {
634         Types values;
635 
636         // generate payload constructors
637         static foreach (i, CT; Types)
638         {
639             this()(auto ref CT val)
640             {
641                 static if (isCopyable!CT) __traits(getMember, Payload, "values")[i] = val;
642                 else static if (isMoveable!CT) __traits(getMember, Payload, "values")[i] = move(val);
643                 else static assert(0, "Can't consume " ~ CT.stringof);
644             }
645 
646             // static if (isCopyable!CT)
647             // {
648             //     this()(auto ref const(CT) val) const { __traits(getMember, Payload, "values")[i] = val; }
649             //     this()(auto ref immutable(CT) val) immutable { __traits(getMember, Payload, "values")[i] = val; }
650             // }
651             // else
652             // {
653             //     @disable this(const(CT) val) const;
654             //     @disable this(immutable(CT) val) immutable;
655             // }
656         }
657     }
658 
659     static if (isRefCountedPayloadEnabled!Hook)
660     {
661         version (D_BetterC) enum enableGCScan = false;
662         else enum enableGCScan = hasIndirections!Payload;
663 
664         // purify memory management functions - see https://github.com/dlang/phobos/pull/4832
665         extern(C) pure nothrow @nogc static
666         {
667             pragma(mangle, "malloc") void* pureMalloc(size_t);
668             pragma(mangle, "free") void pureFree( void *ptr );
669             static if (enableGCScan)
670             {
671                 pragma(mangle, "calloc") void* pureCalloc(size_t nmemb, size_t size);
672                 pragma(mangle, "gc_addRange") void pureGcAddRange( in void* p, size_t sz, const TypeInfo ti = null );
673                 pragma(mangle, "gc_removeRange") void pureGcRemoveRange( in void* p );
674             }
675         }
676 
677         struct Impl
678         {
679             Payload payload;
680             State state;
681             size_t count;
682             static if (isChecked!Hook) bool checked;
683         }
684 
685         void initialize(A...)(auto ref A args)
686         {
687             import std.conv : emplace;
688 
689             allocateStore();
690             emplace(&storage.payload, args);
691             storage.count = 1;
692             storage.state = State.empty;
693             static if (isChecked!Hook) storage.checked = false;
694         }
695 
696         void allocateStore() nothrow pure @trusted
697         {
698             assert(!storage);
699             static if (enableGCScan)
700             {
701                 storage = cast(Impl*) pureCalloc(1, Impl.sizeof);
702                 if (!storage) assert(0, "Memory allocation failed");
703                 pureGcAddRange(&storage.payload, Payload.sizeof);
704             }
705             else
706             {
707                 storage = cast(Impl*) pureMalloc(Impl.sizeof);
708                 if (!storage) assert(0, "Memory allocation failed");
709             }
710         }
711 
712         Impl* storage;
713 
714         @property nothrow @safe pure @nogc
715         size_t refCount() const { return storage !is null ? storage.count : 0; }
716 
717         @property nothrow @safe pure @nogc
718         State state() const { return storage !is null ? storage.state : State.empty; }
719 
720         @property nothrow @safe pure @nogc
721         void state(State state) { assert(storage); storage.state = state; }
722 
723         static if (isChecked!Hook)
724         {
725             @property nothrow @safe pure @nogc
726             bool checked() const { assert(storage); return storage.checked; }
727 
728             @property nothrow @safe pure @nogc
729             void checked(bool ch) { assert(storage); storage.checked = ch; }
730         }
731 
732         ref inout(E) getError()() inout
733         {
734             assert(storage);
735             static if (__VERSION__ < 2078) // workaround - see: https://issues.dlang.org/show_bug.cgi?id=15094
736             {
737                 auto p = &storage.payload;
738                 static if (Types.length == 1) return __traits(getMember, p, "values")[0];
739                 else return __traits(getMember, p, "values")[1];
740             }
741             else
742             {
743                 static if (Types.length == 1) return __traits(getMember, storage.payload, "values")[0];
744                 else return __traits(getMember, storage.payload, "values")[1];
745             }
746         }
747 
748         static if (!is(T == void))
749         {
750             ref inout(T) getValue()() inout
751             {
752                 assert(storage);
753                 static if (__VERSION__ < 2078) // workaround - see: https://issues.dlang.org/show_bug.cgi?id=15094
754                 {
755                     auto p = &storage.payload;
756                     return __traits(getMember, p, "values")[0];
757                 }
758                 else return __traits(getMember, storage.payload, "values")[0];
759             }
760         }
761 
762         this(this) @safe pure nothrow @nogc
763         {
764             if (!storage) return;
765             ++storage.count;
766         }
767     }
768     else
769     {
770         Payload storage;
771         State state = State.empty;
772         static if (isChecked!Hook) bool checked = false;
773 
774         ref inout(E) getError()() inout
775         {
776             static if (Types.length == 1) return __traits(getMember, storage, "values")[0];
777             else return __traits(getMember, storage, "values")[1];
778         }
779 
780         static if (!is(T == void))
781         {
782             ref inout(T) getValue()() inout
783             {
784                 return __traits(getMember, storage, "values")[0];
785             }
786         }
787     }
788 
789     enum State : ubyte { empty, value, error }
790 
791     void setState(MT)(State known = State.empty)
792     {
793         State s;
794         if (known != State.empty) s = known;
795         else
796         {
797             static if (Types.length == 1 && is(T == void)) s = State.error;
798             else static if (Types.length == 1 || is(MT == T)) s = State.value;
799             else s = State.error;
800         }
801 
802         //TODO: change with Hook?
803         assert(state == State.empty || state == s, "Can't change meaning of already set Expected type");
804         state = s;
805     }
806 }
807 
808 /++ Template to determine if hook enables or disables copy constructor.
809 
810     It is enabled by default.
811 
812     See $(LREF hasOnUnchecked) handler, which can be used in combination with disabled
813     copy constructor to enforce that the result is checked.
814 
815     $(WARNING If copy constructor is disabled, it severely limits function chaining
816     as $(LREF Expected) needs to be passed as rvalue in that case.)
817 +/
818 template isCopyConstructorEnabled(Hook)
819 {
820     static if (__traits(hasMember, Hook, "enableCopyConstructor"))
821     {
822         static assert(
823             is(typeof(__traits(getMember, Hook, "enableCopyConstructor")) : bool),
824             "Hook's enableCopyConstructor is expected to be of type bool"
825         );
826         enum isCopyConstructorEnabled = __traits(getMember, Hook, "enableCopyConstructor");
827     }
828     else enum isCopyConstructorEnabled = true;
829 }
830 
831 ///
832 @("isCopyConstructorEnabled")
833 unittest
834 {
835     struct Foo {}
836     struct Bar { static immutable bool enableCopyConstructor = false; }
837     static assert(isCopyConstructorEnabled!Foo);
838     static assert(!isCopyConstructorEnabled!Bar);
839 }
840 
841 /++ Template to determine if hook defines that the $(LREF Expected) storage should
842     use refcounted state storage.
843 
844     If this is enabled, payload is mallocated on the heap and dealocated with the
845     destruction of last $(Expected) instance.
846 
847     See $(LREF hasOnUnchecked) handler, which can be used in combination with refcounted
848     payload to enforce that the result is checked.
849 +/
850 template isRefCountedPayloadEnabled(Hook)
851 {
852     static if (__traits(hasMember, Hook, "enableRefCountedPayload"))
853     {
854         static assert(
855             is(typeof(__traits(getMember, Hook, "enableRefCountedPayload")) : bool),
856             "Hook's enableCopyConstructor is expected to be of type bool"
857         );
858         enum isRefCountedPayloadEnabled = __traits(getMember, Hook, "enableRefCountedPayload");
859         static assert (
860             !isRefCountedPayloadEnabled || isCopyConstructorEnabled!Hook,
861             "Refcounted payload wouldn't work without copy constructor enabled"
862         );
863     }
864     else enum isRefCountedPayloadEnabled = false;
865 }
866 
867 ///
868 @("isRefCountedPayloadEnabled")
869 unittest
870 {
871     struct Foo {}
872     struct Bar {
873         static immutable bool enableCopyConstructor = false;
874         static immutable bool enableRefCountedPayload = true;
875     }
876     struct Hook { static immutable bool enableRefCountedPayload = true; }
877     static assert(!isRefCountedPayloadEnabled!Foo);
878     static assert(!__traits(compiles, isRefCountedPayloadEnabled!Bar));
879     static assert(isRefCountedPayloadEnabled!Hook);
880 }
881 
882 // just a helper to determine check behavior
883 private template isChecked(Hook)
884 {
885     enum isChecked = !isCopyConstructorEnabled!Hook || isRefCountedPayloadEnabled!Hook;
886 }
887 
888 /// Template to determine if provided Hook enables default constructor for $(LREF Expected)
889 template isDefaultConstructorEnabled(Hook)
890 {
891     static if (__traits(hasMember, Hook, "enableDefaultConstructor"))
892     {
893         static assert(
894             is(typeof(__traits(getMember, Hook, "enableDefaultConstructor")) : bool),
895             "Hook's enableDefaultConstructor is expected to be of type bool"
896         );
897         enum isDefaultConstructorEnabled = __traits(getMember, Hook, "enableDefaultConstructor");
898     }
899     else enum isDefaultConstructorEnabled = false;
900 }
901 
902 ///
903 @("isDefaultConstructorEnabled")
904 unittest
905 {
906     struct Foo {}
907     struct Bar { static immutable bool enableDefaultConstructor = true; }
908     static assert(!isDefaultConstructorEnabled!Foo);
909     static assert(isDefaultConstructorEnabled!Bar);
910 }
911 
912 /// Template to determine if provided Hook enables void values for $(LREF Expected)
913 template isVoidValueEnabled(Hook)
914 {
915     static if (__traits(hasMember, Hook, "enableVoidValue"))
916     {
917         static assert(
918             is(typeof(__traits(getMember, Hook, "enableVoidValue")) : bool),
919             "Hook's enableVoidValue is expected to be of type bool"
920         );
921         enum isVoidValueEnabled = __traits(getMember, Hook, "isVoidValueEnabled");
922     }
923     else enum isVoidValueEnabled = true;
924 }
925 
926 ///
927 @("isVoidValueEnabled")
928 unittest
929 {
930     struct Hook { static immutable bool enableVoidValue = false; }
931     assert(!ok().hasError); // void values are enabled by default
932     static assert(!__traits(compiles, ok!(string, Hook)())); // won't compile
933 }
934 
935 /// Template to determine if hook provides function called on empty value.
936 template hasOnAccessEmptyValue(Hook, E)
937 {
938     static if (__traits(hasMember, Hook, "onAccessEmptyValue"))
939     {
940         static assert(
941             is(typeof(__traits(getMember, Hook, "onAccessEmptyValue")(E.init))),
942             "Hook's onAccessEmptyValue is expected to be callable with error value type"
943         );
944         enum hasOnAccessEmptyValue = true;
945     }
946     else enum hasOnAccessEmptyValue = false;
947 }
948 
949 ///
950 @("hasOnAccessEmptyValue")
951 unittest
952 {
953     struct Foo {}
954     struct Bar { static void onAccessEmptyValue(E)(E err) {} }
955     static assert(!hasOnAccessEmptyValue!(Foo, string));
956     static assert(hasOnAccessEmptyValue!(Bar, string));
957 }
958 
959 /++ Template to determine if hook provides function called on empty error.
960 +/
961 template hasOnAccessEmptyError(Hook)
962 {
963     static if (__traits(hasMember, Hook, "onAccessEmptyError"))
964     {
965         static assert(
966             is(typeof(__traits(getMember, Hook, "onAccessEmptyError")())),
967             "Hook's onAccessEmptyValue is expected to be callable with no arguments"
968         );
969         enum hasOnAccessEmptyError = true;
970     }
971     else enum hasOnAccessEmptyError = false;
972 }
973 
974 ///
975 @("hasOnAccessEmptyError")
976 unittest
977 {
978     struct Foo {}
979     struct Bar { static void onAccessEmptyError() {} }
980     static assert(!hasOnAccessEmptyError!Foo);
981     static assert(hasOnAccessEmptyError!Bar);
982 }
983 
984 /++ Template to determine if hook provides custom handler for case
985     when the $(LREF Expected) result is not checked.
986 
987     For this to work it currently also has to pass $(LREF isCopyConstructorEnabled)
988     as this is implemented by simple flag controled on $(LREF Expected) destructor.
989 +/
990 template hasOnUnchecked(Hook)
991 {
992     static if (__traits(hasMember, Hook, "onUnchecked"))
993     {
994         static assert(
995             is(typeof(__traits(getMember, Hook, "onUnchecked")())),
996             "Hook's onUnchecked is expected to be callable with no arguments"
997         );
998         static assert(
999             !isCopyConstructorEnabled!Hook || isRefCountedPayloadEnabled!Hook,
1000             "For unchecked check to work, it is needed to also have disabled copy constructor or enabled reference counted payload"
1001         );
1002         enum hasOnUnchecked = true;
1003     }
1004     else enum hasOnUnchecked = false;
1005 }
1006 
1007 version (D_Exceptions)
1008 {
1009     ///
1010     @("hasOnUnchecked")
1011     @system unittest
1012     {
1013         struct Foo {}
1014         struct Bar { static void onUnchecked() { } }
1015         struct Hook {
1016             static immutable bool enableCopyConstructor = false;
1017             static void onUnchecked() @safe { throw new Exception("result unchecked"); }
1018         }
1019 
1020         // template checks
1021         static assert(!hasOnUnchecked!Foo);
1022         static assert(!__traits(compiles, hasOnUnchecked!Bar)); // missing disabled constructor
1023         static assert(hasOnUnchecked!Hook);
1024 
1025         // copy constructor
1026         auto exp = ok!(string, Hook)(42);
1027         auto exp2 = err!(int, Hook)("foo");
1028         static assert(!__traits(compiles, exp.andThen(ok!(string, Hook)(42)))); // disabled cc
1029         assert(exp.andThen(exp2).error == "foo"); // passed by ref so no this(this) called
1030 
1031         // check for checked result
1032         assertThrown({ ok!(string, Hook)(42); }());
1033         assertThrown({ err!(void, Hook)("foo"); }());
1034     }
1035 }
1036 
1037 /++ Template to determine if hook provides function called when value is set.
1038 +/
1039 template hasOnValueSet(Hook, T)
1040 {
1041     static if (__traits(hasMember, Hook, "onValueSet"))
1042     {
1043         static assert(
1044             is(typeof(__traits(getMember, Hook, "onValueSet")(T.init))),
1045             "Hook's onValueSet is expected to be callable with value argument"
1046         );
1047         enum hasOnValueSet = true;
1048     }
1049     else enum hasOnValueSet = false;
1050 }
1051 
1052 ///
1053 @("hasOnValueSet")
1054 unittest
1055 {
1056     struct Hook {
1057         static int lastValue;
1058         static void onValueSet(T)(auto ref T val) { lastValue = val; }
1059     }
1060 
1061     static assert(hasOnValueSet!(Hook, int));
1062     auto res = ok!(string, Hook)(42);
1063     assert(res.hasValue);
1064     assert(Hook.lastValue == 42);
1065 }
1066 
1067 /++ Template to determine if hook provides function called when error is set.
1068 +/
1069 template hasOnErrorSet(Hook, T)
1070 {
1071     static if (__traits(hasMember, Hook, "onErrorSet"))
1072     {
1073         static assert(
1074             is(typeof(__traits(getMember, Hook, "onErrorSet")(T.init))),
1075             "Hook's onErrorSet is expected to be callable with error argument"
1076         );
1077         enum hasOnErrorSet = true;
1078     }
1079     else enum hasOnErrorSet = false;
1080 }
1081 
1082 ///
1083 @("hasOnErrorSet")
1084 unittest
1085 {
1086     struct Hook {
1087         static string lastErr;
1088         static void onErrorSet(E)(auto ref E err) { lastErr = err; }
1089     }
1090 
1091     static assert(hasOnErrorSet!(Hook, string));
1092     auto res = err!(int, Hook)("foo");
1093     assert(res.hasError);
1094     assert(Hook.lastErr == "foo");
1095 }
1096 
1097 /++ Default hook implementation for $(LREF Expected)
1098 +/
1099 struct Abort
1100 {
1101 static:
1102     /++ Default constructor for $(LREF Expected) is disabled.
1103         Same with the `opAssign`, so $(LREF Expected) can be only constructed
1104         once and not modified afterwards.
1105     +/
1106     immutable bool enableDefaultConstructor = false;
1107 
1108     /// Handler for case when empty value is accessed.
1109     void onAccessEmptyValue(E)(E err) nothrow @nogc
1110     {
1111         assert(0, "Value not set");
1112     }
1113 
1114     /// Handler for case when empty error is accessed.
1115     void onAccessEmptyError() nothrow @nogc
1116     {
1117         assert(0, "Error not set");
1118     }
1119 }
1120 
1121 ///
1122 @("Abort")
1123 @system unittest
1124 {
1125     static assert(!isDefaultConstructorEnabled!Abort);
1126     static assert(hasOnAccessEmptyValue!(Abort, string));
1127     static assert(hasOnAccessEmptyValue!(Abort, int));
1128     static assert(hasOnAccessEmptyError!Abort);
1129 
1130     version (D_Exceptions)
1131     {
1132         assertThrown!Throwable(ok(42).error);
1133         assertThrown!Throwable(err!int("foo").value);
1134     }
1135 }
1136 
1137 version (D_Exceptions)
1138 {
1139     /++ Hook implementation that throws exceptions instead of default assert behavior.
1140     +/
1141     struct Throw
1142     {
1143     static:
1144 
1145         /++ Default constructor for $(LREF Expected) is disabled.
1146             Same with the `opAssign`, so $(LREF Expected) can be only constructed
1147             once and not modified afterwards.
1148         +/
1149         immutable bool enableDefaultConstructor = false;
1150 
1151         /++ Handler for case when empty value is accessed.
1152 
1153             Throws:
1154                 If `E` inherits from `Throwable`, the error value is thrown.
1155                 Otherwise, an [Unexpected] instance containing the error value is
1156                 thrown.
1157         +/
1158         void onAccessEmptyValue(E)(E err)
1159         {
1160             import std.traits : Unqual;
1161             static if(is(Unqual!E : Throwable)) throw err;
1162             else throw new Unexpected!E(err);
1163         }
1164 
1165         /// Handler for case when empty error is accessed.
1166         void onAccessEmptyError()
1167         {
1168             throw new Unexpected!string("Can't access error on expected value");
1169         }
1170     }
1171 
1172     ///
1173     @("Throw")
1174     unittest
1175     {
1176         static assert(!isDefaultConstructorEnabled!Throw);
1177         static assert(hasOnAccessEmptyValue!(Throw, string));
1178         static assert(hasOnAccessEmptyValue!(Throw, int));
1179         static assert(hasOnAccessEmptyError!Throw);
1180 
1181         assertThrown!(Unexpected!string)(ok!(string, Throw)(42).error);
1182         assertThrown!(Unexpected!string)(err!(int, Throw)("foo").value);
1183         assertThrown!(Unexpected!int)(err!(bool, Throw)(-1).value);
1184     }
1185 
1186     /++ Hook implementation that behaves like a thrown exception.
1187         It throws $(D Exception) right when the $(LREF Expected) with error is initialized.
1188 
1189         With this, one can easily change the code behavior between `Expected` idiom or plain `Exception`s.
1190     +/
1191     struct AsException
1192     {
1193     static:
1194 
1195         /++ Default constructor for $(LREF Expected) is disabled.
1196             Same with the `opAssign`, so $(LREF Expected) can be only constructed
1197             once and not modified afterwards.
1198         +/
1199         immutable bool enableDefaultConstructor = false;
1200 
1201         /++ Handler for case when empty error is accessed.
1202         +/
1203         void onErrorSet(E)(auto ref E err)
1204         {
1205             static if (is(E : Throwable)) throw E;
1206             else throw new Unexpected!E(err);
1207         }
1208     }
1209 
1210     ///
1211     @("AsException")
1212     unittest
1213     {
1214         static assert(!isDefaultConstructorEnabled!AsException);
1215         static assert(hasOnErrorSet!(AsException, string));
1216 
1217         auto div(int a, int b) {
1218             if (b != 0) return ok!(string, AsException)(a / b);
1219             return err!(int, AsException)("oops");
1220         }
1221 
1222         assert(div(10, 2) == 5);
1223         assert(collectExceptionMsg!(Unexpected!string)(div(1, 0)) == "oops");
1224     }
1225 }
1226 
1227 /++ Hook implementation that behaves same as $(LREF Abort) hook, but uses refcounted payload
1228     instead, which also enables us to check, if the result was properly checked before it is
1229     discarded.
1230 +/
1231 struct RCAbort
1232 {
1233 static:
1234     /++ Default constructor for $(LREF Expected) is disabled.
1235         Same with the `opAssign`, so $(LREF Expected) can be only constructed
1236         once and not modified afterwards.
1237     +/
1238     immutable bool enableDefaultConstructor = false;
1239 
1240     /// Copy constructor is enabled so the reference counting makes sense
1241     immutable bool enableCopyConstructor = true;
1242 
1243     /// Enabled reference counted payload
1244     immutable bool enableRefCountedPayload = true;
1245 
1246     void onUnchecked() pure nothrow @nogc { assert(0, "result unchecked"); }
1247 }
1248 
1249 ///
1250 @("RCAbort")
1251 @system unittest
1252 {
1253     // behavior checks
1254     static assert(!isDefaultConstructorEnabled!RCAbort);
1255     static assert(isCopyConstructorEnabled!RCAbort);
1256     static assert(isRefCountedPayloadEnabled!RCAbort);
1257 
1258     // basics
1259     assert(ok!(string, RCAbort)(42) == 42);
1260     assert(err!(int, RCAbort)("foo").error == "foo");
1261 
1262     // checked
1263     {
1264         auto res = ok!(string, RCAbort)(42);
1265         assert(!res.checked);
1266         assert(res);
1267         assert(res.checked);
1268     }
1269 
1270     // unchecked - throws assert
1271     version (D_Exceptions) assertThrown!Throwable({ ok!(string, RCAbort)(42); }());
1272 
1273     {
1274         auto res = ok!(string, RCAbort)(42);
1275         {
1276             auto res2 = res;
1277             assert(!res.checked);
1278             assert(res.refCount == 2);
1279             assert(res2.refCount == 2);
1280         }
1281         assert(res.refCount == 1);
1282         assert(res.hasValue);
1283     }
1284 
1285     // chaining
1286     assert(err!(int, RCAbort)("foo").orElse!(() => ok!(string, RCAbort)(42)) == 42);
1287     assert(ok!(string, RCAbort)(42).andThen!(() => err!(int, RCAbort)("foo")).error == "foo");
1288     version (D_Exceptions)
1289     {
1290         assertThrown!Throwable(err!(int, RCAbort)("foo").orElse!(() => ok!(string, RCAbort)(42)));
1291         assertThrown!Throwable(ok!(string, RCAbort)(42).andThen!(() => err!(int, RCAbort)("foo")));
1292     }
1293 }
1294 
1295 version (D_Exceptions)
1296 {
1297     /++ An exception that represents an error value.
1298 
1299         This is used by $(LREF Throw) hook when undefined value or error is
1300         accessed on $(LREF Expected)
1301     +/
1302     class Unexpected(T) : Exception
1303     {
1304         // remove possible inout qualifier
1305         static if (is(T U == inout U)) alias ET = U;
1306         else alias ET = T;
1307 
1308         ET error; /// error value
1309 
1310         /// Constructs an `Unexpected` exception from an error value.
1311         pure @safe @nogc nothrow
1312         this()(auto ref T value, string file = __FILE__, size_t line = __LINE__)
1313         {
1314             import std.traits : isAssignable;
1315             static if (isAssignable!(string, T)) super(value, file, line);
1316             else super("Unexpected error", file, line);
1317 
1318             this.error = error;
1319         }
1320     }
1321 }
1322 
1323 /++
1324     Creates an $(LREF Expected) object from an expected value, with type inference.
1325 +/
1326 Expected!(T, E, Hook) ok(E = string, Hook = Abort, T)(auto ref T value)
1327 {
1328     return Expected!(T, E, Hook)(value);
1329 }
1330 
1331 /// ditto
1332 Expected!(void, E, Hook) ok(E = string, Hook = Abort)()
1333 {
1334     return Expected!(void, E, Hook)();
1335 }
1336 
1337 ///
1338 @("Expected from value")
1339 unittest
1340 {
1341     // void
1342     {
1343         auto res = ok();
1344         static assert(is(typeof(res) == Expected!(void, string)));
1345         assert(res);
1346     }
1347 
1348     // int
1349     {
1350         auto res = ok(42);
1351         static assert(is(typeof(res) == Expected!(int, string)));
1352         assert(res);
1353         assert(res.value == 42);
1354     }
1355 
1356     // string
1357     {
1358         auto res = ok("42");
1359         static assert(is(typeof(res) == Expected!(string, string)));
1360         assert(res);
1361         assert(res.value == "42");
1362     }
1363 
1364     // other error type
1365     {
1366         auto res = ok!bool(42);
1367         static assert(is(typeof(res) == Expected!(int, bool)));
1368         assert(res);
1369         assert(res.value == 42);
1370     }
1371 }
1372 
1373 /++ Constructs $(LREF Expected) from the result of the provided function.
1374 
1375     If the function is `nothrow`, it just returns it's result using $(LREF Expected).
1376 
1377     If not, then it consumes it's possible $(D Exception) using `try catch` block and
1378     constructs $(LREF Expected) in regards of the result.
1379 +/
1380 template consume(alias fun, Hook = Abort)
1381 {
1382     auto consume(Args...)(auto ref Args args) if (is(typeof(fun(args))))
1383     {
1384         import std.traits : hasFunctionAttributes;
1385 
1386         alias T = typeof(fun(args));
1387         static if (is(hasFunctionAttributes!(fun, "nothrow"))) return ok!Exception(fun(args));
1388         else
1389         {
1390             try return Expected!(T, Exception)(fun(args));
1391             catch (Exception ex) return err!T(ex);
1392         }
1393     }
1394 }
1395 
1396 version (D_Exceptions)
1397 {
1398     ///
1399     @("consume from function call")
1400     unittest
1401     {
1402         auto fn(int v) { if (v == 42) throw new Exception("don't panic"); return v; }
1403 
1404         assert(consume!fn(1) == 1);
1405         assert(consume!fn(42).error.msg == "don't panic");
1406     }
1407 }
1408 
1409 /++
1410     Creates an $(LREF Expected) object from an error value, with type inference.
1411 +/
1412 Expected!(T, E, Hook) err(T = void, Hook = Abort, E)(auto ref E err)
1413 {
1414     static if (Expected!(T, E, Hook).Types.length == 1 && !is(T == void))
1415         return Expected!(T, E, Hook)(err, false);
1416     else return Expected!(T, E, Hook)(err);
1417 }
1418 
1419 ///
1420 @("Expected from error value")
1421 unittest
1422 {
1423     // implicit void value type
1424     {
1425         auto res = err("foo");
1426         static assert(is(typeof(res) == Expected!(void, string)));
1427         assert(!res);
1428         assert(res.error == "foo");
1429     }
1430 
1431     // bool
1432     {
1433         auto res = err!int("42");
1434         static assert(is(typeof(res) == Expected!(int, string)));
1435         assert(!res);
1436         assert(res.error == "42");
1437     }
1438 
1439     // other error type
1440     {
1441         auto res = err!bool(42);
1442         static assert(is(typeof(res) == Expected!(bool, int)));
1443         assert(!res);
1444         assert(res.error == 42);
1445     }
1446 }
1447 
1448 /++ Unwraps a result, yielding the content of expected value.
1449     If there is none, or error value, it throws $(D assert(0)) with the provided message.
1450 
1451     Params:
1452         res     = $(LREF Expected) to check the result of
1453         msg     = message to use with assert
1454         handler = custom handler to be called on error
1455 +/
1456 T expect(EX : Expected!(T, E, H), T, E, H)(auto ref EX res, lazy string msg)
1457 {
1458     //TODO: hook for customization
1459 
1460     static if (!is(T == void)) { if (res.hasValue) return res.value; }
1461     else  { if (!res.hasError) return; }
1462 
1463     version (D_BetterC) assert(0, msg);
1464     else
1465     {
1466         import std.format : format;
1467         if (res.hasError) assert(0, format!"%s: %s"(msg, res.error));
1468         else assert(0, format!"%s: empty"(msg));
1469     }
1470 }
1471 
1472 /// ditto
1473 T expect(alias handler, EX : Expected!(T, E, H), T, E, H)(auto ref EX res)
1474 {
1475     static if (!is(T == void)) { if (res.hasValue) return res.value; }
1476     else  { if (!res.hasError) return; }
1477 
1478     static if (!is(typeof(handler(res.error)) == void))
1479         return handler(res.error);
1480     else
1481     {
1482         handler(res.error);
1483         return T.init;
1484     }
1485 }
1486 
1487 version (D_Exceptions)
1488 {
1489     ///
1490     @("expect")
1491     @system unittest
1492     {
1493         assert(ok(42).expect("oops") == 42);
1494         ok().expect("oops"); // void value
1495         assert(collectExceptionMsg!Throwable(Expected!int.init.expect("oops")) == "oops: empty");
1496         assert(collectExceptionMsg!Throwable(err!int("foo").expect("oops")) == "oops: foo");
1497     }
1498 }
1499 
1500 /++ Unwraps a result, yielding the content of an error value.
1501     If there is none, or success value, it throws $(D assert(0)) with the provided message.
1502 
1503     Params:
1504         res = $(LREF Expected) to check the result of
1505         msg = message to use with assert
1506         handler = custom handler to be called on value
1507 +/
1508 E expectErr(EX : Expected!(T, E, H), T, E, H)(auto ref EX res, lazy string msg)
1509 {
1510     //TODO: hook for customization
1511 
1512     if (res.hasError) return res.error;
1513 
1514     version (D_BetterC) assert(0, msg);
1515     else
1516     {
1517         import std.format : format;
1518         static if (!is(T == void))
1519         {
1520             if (res.hasValue) assert(0, format!"%s: %s"(msg, res.value));
1521         }
1522         assert(0, format!"%s: empty"(msg));
1523     }
1524 }
1525 
1526 /// ditto
1527 E expectErr(alias handler, EX : Expected!(T, E, H), T, E, H)(auto ref EX res)
1528 {
1529     if (res.hasError) return res.error;
1530 
1531     static if (!is(typeof(handler(T.init)) == void))
1532     {
1533         static if (!is(T == void)) return handler(res.hasValue ? res.value : T.init);
1534         else return handler();
1535     }
1536     else
1537     {
1538         static if (!is(T == void)) handler(res.hasValue ? res.value : T.init);
1539         else handler();
1540         return T.init;
1541     }
1542 }
1543 
1544 ///
1545 @("expectErr")
1546 @system unittest
1547 {
1548     assert(err("foo").expectErr("oops") == "foo");
1549     version (D_Exceptions)
1550     {
1551         assert(collectExceptionMsg!Throwable(Expected!int.init.expectErr("oops")) == "oops: empty");
1552         assert(collectExceptionMsg!Throwable(ok(42).expectErr("oops")) == "oops: 42");
1553         assert(collectExceptionMsg!Throwable(ok().expectErr("oops")) == "oops: empty"); // void value
1554     }
1555 
1556     assert(ok("foo").expect!(a => "bar") == "foo");
1557     assert(err!string("foo").expect!(a => "bar") == "bar");
1558     assert(err!string("foo").expect!((a) {}) is null);
1559 
1560     assert(ok("foo").expectErr!(a => "bar") == "bar");
1561     assert(err!string("foo").expectErr!(a => "bar") == "foo");
1562     assert(ok!string("foo").expectErr!((a) {}) is null);
1563 }
1564 
1565 /++
1566     Returns the error contained within the $(LREF Expected) _and then_ another value if there's no error.
1567     This function can be used for control flow based on $(LREF Expected) values.
1568 
1569     Predicate can accept no arguments, variable arguments, or previous result value with additional variable arguments.
1570     It must return $(LREF Expected) wth the same error type. But can provide different value type.
1571 
1572     Params:
1573         exp = The $(LREF Expected) to call andThen on
1574         value = The value to return if there isn't an error
1575         pred = The predicate to call if the there isn't an error
1576 +/
1577 auto ref andThen(EX : Expected!(T, E, H), VEX : Expected!(VT, E, H), T, VT, E, H)(
1578     auto ref EX exp, auto ref VEX value)
1579 {
1580     static if (is(T == VT)) return exp.hasError ? exp : value;
1581     else return exp.hasError ? err!(VT, H)(exp.error) : value;
1582 }
1583 
1584 /// ditto
1585 auto ref andThen(alias pred, EX : Expected!(T, E, H), T, E, H, Args...)(auto ref EX exp, Args args)
1586 {
1587     static if (!is(T == void) && is(typeof(pred(T.init, args)) : Expected!(VT, E, H), VT))
1588     {
1589         static if (is(T == VT)) return exp.hasError ? exp : pred(exp.value, args);
1590         else return exp.hasError ? err!(VT, H)(exp.error) : pred(exp.value, args);
1591     }
1592     else static if (is(typeof(pred(args)) : Expected!(VT, E, H), VT))
1593     {
1594         static if (is(T == VT)) return exp.hasError ? exp : pred(args);
1595         else return exp.hasError ? err!(VT, H)(exp.error) : pred(args);
1596     }
1597     else
1598     {
1599         static assert(0, "Expected predicate of Expected type with optional args receiving previous value");
1600     }
1601 }
1602 
1603 ///
1604 @("andThen")
1605 unittest
1606 {
1607     import std.format : format;
1608 
1609     assert(ok(42).andThen(ok(1)) == 1);
1610     assert(ok(42).andThen!(() => ok(0)) == 0);
1611     assert(ok(42).andThen(err!int("foo")).error == "foo");
1612     assert(ok(42).andThen!(() => err!int("foo")).error == "foo");
1613     assert(err!int("foo").andThen(ok(42)).error == "foo");
1614     assert(err!int("foo").andThen!(() => ok(42)).error == "foo");
1615     assert(err!int("foo").andThen(err!int("bar")).error == "foo");
1616     assert(err!int("foo").andThen!(() => err!int("bar")).error == "foo");
1617 
1618     // with void value
1619     assert(ok().andThen!(() => ok()));
1620     assert(ok().andThen!(() => err("foo")).error == "foo");
1621     assert(err("foo").andThen!(() => ok()).error == "foo");
1622 
1623     // with different value type
1624     assert(ok(42).andThen(ok("foo")) == "foo");
1625     assert(err!int("bug").andThen(ok("foo")).error == "bug");
1626     assert(ok(42).andThen!(() => err!bool("foo")).error == "foo");
1627     assert(err!int("bug").andThen!(() => err!bool("foo")).error == "bug");
1628 
1629     // with args
1630     assert(ok(42).andThen!((v) => err!bool(v))("foo").error == "foo"); // doesn't use previous value
1631     version (D_BetterC)
1632         assert(ok(42).andThen!((i, v)
1633             {
1634                 assert(i == 42);
1635                 assert(v == "foo");
1636                 return err!bool("betterc");
1637             })("foo").error == "betterc"); // pass previous value to predicate
1638     else
1639         assert(ok(42).andThen!((i, v) => err!bool(format!"%s: %s"(v, i)))("foo").error == "foo: 42"); // pass previous value to predicate
1640     assert(ok().andThen!((v) => ok(v))(42) == 42); // void type on first ok
1641 }
1642 
1643 /++
1644     Returns the value contained within the $(LREF Expected) _or else_ another value if there's an error.
1645     This function can be used for control flow based on $(LREF Expected) values.
1646 
1647     Predicate can accept no arguments, variable arguments, or previous result error value with additional variable arguments.
1648     It must return $(LREF Expected) wth the same value type. But can provide different error value type.
1649 
1650     Params:
1651         exp = The $(LREF Expected) to call orElse on
1652         value = The value to return if there is an error
1653         pred = The predicate to call if the there is an error
1654 +/
1655 auto ref U orElse(EX, U)(auto ref EX exp, lazy U value)
1656     if (is(EX : Expected!(T, E, H), T, E, H) && is(U : T))
1657 {
1658     return exp.orElse!value;
1659 }
1660 
1661 /// ditto
1662 auto ref orElse(alias pred, EX : Expected!(T, E, H), T, E, H, Args...)(
1663     auto ref EX exp, Args args)
1664 {
1665     static if (is(typeof(pred(args)) : T))
1666         return exp.hasError ? pred(args) : exp.value;
1667     else static if (is(typeof(pred(exp.error, args)) : T))
1668         return exp.hasError ? pred(exp.error, args) : exp.value;
1669     else static if (is(typeof(pred(args)) : Expected!(T, VE, H), VE))
1670     {
1671         static if (is(E == VE)) return exp.hasError ? pred(args) : exp;
1672         else return exp.hasError ? pred(args) : ok!VE(exp.value);
1673     }
1674     else static if (is(typeof(pred(exp.error, args)) : Expected!(T, VE, H), VE))
1675     {
1676         static if (is(E == VE)) return exp.hasError ? pred(exp.error, args) : exp;
1677         else return exp.hasError ? pred(exp.error, args) : ok!VE(exp.value);
1678     }
1679     else static assert(0, "Expecting predicate of same value type");
1680 }
1681 
1682 ///
1683 @("orElse")
1684 unittest
1685 {
1686     assert(ok(42).orElse(0) == 42);
1687     assert(ok(42).orElse!(() => 0) == 42);
1688     assert(err!int("foo").orElse(0) == 0);
1689     assert(err!int("foo").orElse!(() => 0) == 0);
1690     assert(ok(42).orElse!(() => ok(0)) == 42);
1691     assert(err!int("foo").orElse!(() => ok(42)) == 42);
1692     assert(err!int("foo").orElse!(() => err!int("bar")).error == "bar");
1693 
1694     // with void value
1695     assert(ok().orElse!(() => err("foo")));
1696     assert(err("foo").orElse!(() => ok()));
1697     assert(err("foo").orElse!(() => err("bar")).error == "bar");
1698 
1699     // with args
1700     assert(err!int("foo").orElse!((v) => v)(42) == 42);
1701 
1702     // with different error type
1703     assert(err!int("foo").orElse!((v) => ok!int(v))(42).value == 42); // string -> int
1704     assert(err!int("foo").orElse!((v) => err!int(v))(42).error == 42);
1705     assert(err!int("foo").orElse!((e, v) => err!int(e.length + v))(42).error == 45); // with previous error
1706 }
1707 
1708 /++
1709     Applies a function to the expected value in an $(LREF Expected) object.
1710 
1711     If no expected value is present, the original error value is passed through
1712     unchanged, and the function is not called.
1713 
1714     Params:
1715         op = function called to map $(LREF Expected) value
1716         hook = use another hook for mapped $(LREF Expected)
1717 
1718     Returns:
1719         A new $(LREF Expected) object containing the result.
1720 +/
1721 template map(alias op, Hook = Abort)
1722 {
1723     /++
1724         The actual `map` function.
1725 
1726         Params:
1727             self = an [Expected] object
1728     +/
1729     auto map(T, E, H)(auto ref Expected!(T, E, H) self)
1730         if ((is(T == void) && is(typeof(op()))) || (!is(T == void) && is(typeof(op(self.value)))))
1731     {
1732         static if (is(T == void)) alias U = typeof(op());
1733         else alias U = typeof(op(self.value));
1734 
1735         if (self.hasError) return err!(U, Hook)(self.error);
1736         else
1737         {
1738             static if (is(T == void)) return ok!(E, Hook)(op());
1739             else return ok!(E, Hook)(op(self.value));
1740         }
1741     }
1742 }
1743 
1744 ///
1745 @("map")
1746 unittest
1747 {
1748     {
1749         assert(ok(42).map!(a => a/2).value == 21);
1750         assert(ok().map!(() => 42).value == 42);
1751         assert(err!int("foo").map!(a => 42).error == "foo");
1752         assert(err("foo").map!(() => 42).error == "foo");
1753     }
1754 
1755     // remap hook
1756     {
1757         static struct Hook {}
1758         auto res = ok(42).map!(a => a/2, Hook);
1759         assert(res == 21);
1760         static assert(is(typeof(res) == Expected!(int, string, Hook)));
1761     }
1762 }
1763 
1764 /++
1765     Applies a function to the expected error in an $(LREF Expected) object.
1766 
1767     If no error is present, the original value is passed through
1768     unchanged, and the function is not called.
1769 
1770     Params:
1771         op = function called to map $(LREF Expected) error
1772         hook = use another hook for mapped $(LREF Expected)
1773 
1774     Returns:
1775         A new $(LREF Expected) object containing the result.
1776 +/
1777 template mapError(alias op, Hook = Abort)
1778 {
1779     /++
1780         The actual `mapError` function.
1781 
1782         Params:
1783             self = an [Expected] object
1784     +/
1785     auto mapError(T, E, H)(auto ref Expected!(T, E, H) self)
1786         if (is(typeof(op(self.error))))
1787     {
1788         alias U = typeof(op(self.error));
1789 
1790         static if (!is(T == void))
1791         {
1792             if (self.hasValue) return ok!(U, Hook)(self.value);
1793         }
1794         return err!(T, Hook)(op(self.error));
1795     }
1796 }
1797 
1798 ///
1799 @("mapError")
1800 unittest
1801 {
1802     {
1803         assert(ok(42).mapError!(e => e).value == 42);
1804         assert(err("foo").mapError!(e => 42).error == 42);
1805         version (D_Exceptions) assert(err("foo").mapError!(e => new Exception(e)).error.msg == "foo");
1806     }
1807 
1808     // remap hook
1809     {
1810         static struct Hook {}
1811         auto res = ok(42).mapError!(e => e, Hook);
1812         assert(res == 42);
1813         static assert(is(typeof(res) == Expected!(int, string, Hook)));
1814 
1815         auto res2 = err!int("foo").mapError!(e => "bar", Hook);
1816         assert(res2.error == "bar");
1817         static assert(is(typeof(res2) == Expected!(int, string, Hook)));
1818     }
1819 }
1820 
1821 /++
1822     Maps a `Expected<T, E>` to `U` by applying a function to a contained value, or a fallback function to a contained error value.
1823 
1824     Both functions has to be of the same return type.
1825 
1826     This function can be used to unpack a successful result while handling an error.
1827 
1828     Params:
1829         valueOp = function called to map $(LREF Expected) value
1830         errorOp = function called to map $(LREF Expected) error
1831         hook = use another hook for mapped $(LREF Expected)
1832 
1833     Returns:
1834         A new $(LREF Expected) object containing the result.
1835 +/
1836 template mapOrElse(alias valueOp, alias errorOp)
1837 {
1838     /++
1839         The actual `mapOrElse` function.
1840 
1841         Params:
1842             self = an [Expected] object
1843     +/
1844     auto mapOrElse(T, E, H)(auto ref Expected!(T, E, H) self)
1845         if (
1846             is(typeof(errorOp(self.error))) &&
1847             (
1848                 (is(T == void) && is(typeof(valueOp()) == typeof(errorOp(self.error)))) ||
1849                 (!is(T == void) && is(typeof(valueOp(self.value)) == typeof(errorOp(self.error))))
1850             )
1851         )
1852     {
1853         alias U = typeof(errorOp(self.error));
1854 
1855         if (self.hasError) return errorOp(self.error);
1856         else
1857         {
1858             static if (is(T == void)) return valueOp();
1859             else return valueOp(self.value);
1860         }
1861     }
1862 }
1863 
1864 ///
1865 @("mapOrElse")
1866 unittest
1867 {
1868     assert(ok(42).mapOrElse!(v => v/2, e => 0) == 21);
1869     assert(ok().mapOrElse!(() => true, e => false));
1870     assert(err!int("foo").mapOrElse!(v => v/2, e => 42) == 42);
1871     assert(!err("foo").mapOrElse!(() => true, e => false));
1872 }