1 /**
2 Copyright: Copyright (c) 2021, Joakim Brännström. All rights reserved.
3 License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0)
4 Author: Joakim Brännström (joakim.brannstrom@gmx.com)
5 */
6 module my.sumtype;
7 
8 public import sumtype;
9 
10 /** Check if an instance of a sumtype contains the specific type.
11  *
12  * This is from the D forum by Paul Backus, the author of sumtype.
13  *
14  * Example:
15  * ---
16  * assert(someType.contains!int);
17  * ---
18  *
19  */
20 bool contains(T, ST)(ST st) if (isSumType!ST) {
21     return st.match!(value =>  is(typeof(value) == T));
22 }
23 
24 @("shall match the sumtype")
25 unittest {
26     alias T = SumType!(int, bool, char);
27     auto a = T(true);
28     assert(a.contains!bool);
29     assert(!a.contains!int);
30 }
31 
32 /** Restrict matching in a sumtype to a bundle of types.
33  *
34  */
35 template restrictTo(Args...) if (Args.length >= 1) {
36     import std.meta : IndexOf = staticIndexOf;
37 
38     alias Types = Args[0 .. $ - 1];
39     alias fun = Args[$ - 1];
40 
41     auto ref restrictTo(T)(auto ref T value) if (IndexOf!(T, Types) >= 0) {
42         import core.lifetime : forward;
43 
44         static assert(IndexOf!(T, Types) >= 0);
45         return fun(forward!value);
46     }
47 }
48 
49 @("shall restrict matching")
50 unittest {
51     static struct Foo0 {
52     }
53 
54     static struct Foo1 {
55     }
56 
57     static struct Foo2 {
58     }
59 
60     static struct Foo3 {
61     }
62 
63     static struct Foo4 {
64     }
65 
66     static struct Bar0 {
67     }
68 
69     static struct Bar1 {
70     }
71 
72     static struct Bar2 {
73     }
74 
75     SumType!(Foo0, Foo1, Foo2, Foo3, Foo4, Bar0, Bar1, Bar2) someType;
76 
77     someType.match!(restrictTo!(Foo0, Foo1, Foo2, Foo3, val => {}),
78             restrictTo!(Bar0, Bar1, Bar2, val => {}), _ => {});
79 }
80 
81 /// For ignoring types.
82 void ignore(T)(T) {
83 }
84 
85 // TODO: why doesn't this work?
86 //void ignore(T)(auto ref T) {}
87 
88 @("shall ignore the type")
89 unittest {
90     static struct A {
91     }
92 
93     static struct B {
94     }
95 
96     static struct C {
97     }
98 
99     SumType!(A, B, C) obj;
100 
101     ignore(A.init);
102 
103     //You can instantiate it explicitly to ignore a specific type:
104     obj.match!(ignore!A, (B b) {}, (C c) {});
105 
106     //// Or you can use it as a catch-all handler:
107     obj.match!((A a) {}, ignore);
108 }
109 
110 /** All return types from `Args`.
111  */
112 template AllReturn(Args...) if (Args.length >= 1) {
113     import std.meta : AliasSeq;
114     import std.traits : ReturnType;
115 
116     static if (Args.length == 1) {
117         alias AllReturn = ReturnType!(Args[0]);
118     } else {
119         alias AllReturn = AliasSeq!(ReturnType!(Args[0]), AllReturn!(Args[1 .. $]));
120     }
121 }
122 
123 alias SumTypeFromReturn(T...) = SumType!(AllReturn!T);
124 
125 @("shall make a sumtype from the return types")
126 unittest {
127     int fn1() {
128         return 0;
129     }
130 
131     double fn2() {
132         return 0.0;
133     }
134 
135     SumTypeFromReturn!(fn1, fn2) obj;
136     obj.match!((int x) {}, (double x) {});
137 }