1 module dpromise.promise;
2 
3 import std.traits;
4 import std.variant : Algebraic, tryVisit, visit;
5 
6 private template ResolveFunc(T) {
7   static if(!is(T == void)) {
8     alias ResolveFunc = void delegate(T) nothrow;
9   }else {
10     alias ResolveFunc = void delegate() nothrow;
11   }
12 }
13 private alias RejectFunc(T) = void delegate(Exception) nothrow;
14 public Promise!T promise(T)(void delegate(ResolveFunc!T resolve, RejectFunc!T reject) executer) nothrow
15 if(!is(Unqual!T : Exception) && !is(Unqual!T : Promise!K, K))
16 in {
17   assert(executer !is null);
18 } body {
19 return new Promise!T((ret) nothrow {
20 
21   static if(!is(T == void)) {
22     void resolve(T v) nothrow {
23       Either!T a = v;
24       ret(a);
25     }
26   }else {
27     void resolve() nothrow {
28       ret(Either!T.init);
29     }
30   }
31   void reject(Exception e) nothrow {
32     Either!T a = e;
33     ret(a);
34   }
35 
36   try{
37     executer(&resolve, &reject);
38   }catch(Exception e) {
39     reject(e);
40   }
41 });}
42 
43 
44 alias Either(T) = Algebraic!(T, Exception);
45 
46 public abstract class Awaiter {
47   abstract @property @safe /*@nogc*/ nothrow {
48     bool isPending() const nothrow;
49     bool isFulfilled() const nothrow;
50     bool isRejected() const nothrow;
51   }
52 
53   abstract public Awaiter then(void delegate() onFulfillment, void delegate(Exception) onRejection) nothrow;
54 }
55 
56 public final class Promise(T) : Awaiter if(!is(Unqual!T : Exception) && !is(Unqual!T : Promise!K, K)) {
57   protected {
58     Either!T _value;
59     static if(is(T == void)) bool _isPending = true;
60     void delegate() nothrow next;
61   }
62 
63   @property @trusted /*@nogc*/ {
64     override bool isPending() const nothrow {
65       static if(!is(T == void)) {
66         return !_value.hasValue;
67       }else {
68         return !_value.hasValue && this._isPending;
69       }
70     }
71 
72     override bool isFulfilled() const nothrow {
73       return !this.isPending && _value.type !is typeid(Exception);
74     }
75 
76     override bool isRejected() const nothrow {
77       return !this.isPending && _value.type is typeid(Exception);
78     }
79     static if(!is(T == void))
80     inout(Unqual!T) value() inout {
81       if(this.isFulfilled) {
82         return _value.get!(Unqual!T);
83       }else {
84         assert(0);
85       }
86     }
87 
88     inout(Exception) exception() inout {
89       if(this.isRejected) {
90         return _value.get!Exception;
91       }else {
92         assert(0);
93       }
94     }
95   }
96 
97   private this() @safe nothrow {
98     this.next = (){};
99   }
100 
101   package(dpromise) this(void delegate(void delegate(Either!T) nothrow) nothrow executer) nothrow {
102     void ret(Either!T v) nothrow {
103       if(!this.isPending) return;
104       try {
105         this._value = v;
106       }catch(Exception e){} //例外は発生しない
107       static if(is(T == void)) this._isPending = false;
108       this.next();
109     }
110     this();
111     executer(&ret);
112   }
113 
114   private template Flatten(S) {
115     static if(is(Unqual!S : Promise!U, U)) {
116       alias Flatten = U;
117     }else {
118       alias Flatten = S;
119     }
120   }
121 
122   override Promise!void then(void delegate() onFulfillment, void delegate(Exception) onRejection) nothrow {
123     return thenImpl(onFulfillment, onRejection);
124   }
125 
126   static if(!is(T == void)) {
127     public Promise!(Flatten!S) then(S, U)(
128       S delegate(T) onFulfillment,
129       U delegate(Exception) onRejection = cast(S delegate(Exception))null
130     ) nothrow if(is(Flatten!S == Flatten!U))
131     in {
132       assert(onFulfillment !is null);
133     }body {
134       if(onRejection is null) {
135         onRejection = (e){ throw e; };
136       }
137       return thenImpl(
138         () {
139           return onFulfillment(this.value);
140         },
141         onRejection
142       );
143     }
144   }
145 
146   public Promise!(Flatten!S) then(S, U)(
147     S delegate() onFulfillment,
148     U delegate(Exception) onRejection = cast(S delegate(Exception))null
149   ) nothrow if(is(Flatten!S == Flatten!U))
150   in {
151     assert(onFulfillment !is null);
152   }body {
153 
154     if(onRejection is null) {
155       onRejection = (e){ throw e; };
156     }
157     return thenImpl(
158       onFulfillment, onRejection
159     );
160   }
161 
162   public Promise!T fail(T delegate(Exception) onRejection) nothrow {
163     if(onRejection is null) {
164       onRejection = (e){ throw e; };
165     }
166     return thenImpl(
167       () @safe {
168         static if(!is(T == void)) return this.value;
169       },
170       onRejection
171     );
172   }
173 
174   private Promise!(Flatten!S) thenImpl(S, U)(
175     S delegate() onFulfillment,
176     U delegate(Exception) onRejection
177   ) nothrow if(is(Flatten!S == Flatten!U))
178   in {
179     assert(onFulfillment !is null);
180     assert(onRejection !is null);
181   }body {
182     auto child = new Promise!(Flatten!S)();
183     this.next = () nothrow {
184       void fulfill() {
185         static if(!is(S : Promise!K, K)) {
186           static if(!is(Flatten!S == void)) {
187             child._value = onFulfillment();
188           }else {
189             onFulfillment();
190             child._isPending = false;
191           }
192           child.next();
193         }else {
194           static if(!is(Flatten!S == void)) {
195             onFulfillment().then(
196               (v) {
197                 child._value = v;
198                 child.next();
199               },
200               (e) {
201                 child._value = e;
202                 child.next();
203               }
204             );
205           }else {
206             onFulfillment().then(
207               () {
208                 child._isPending = false;
209                 child.next();
210               },
211               (e) {
212                 child._value = e;
213                 child.next();
214               }
215             );
216           }
217         }
218       }
219 
220       void reject(Exception exception) {
221         static if(!is(U : Promise!K, K)) {
222           static if(!is(Flatten!U == void)) {
223             child._value = onRejection(exception);
224           }else {
225             onRejection(exception);
226             child._isPending = false;
227           }
228           child.next();
229         }else {
230           static if(!is(Flatten!U == void)) {
231             onRejection(exception).then(
232               (v) {
233                 child._value = v;
234                 child.next();
235               },
236               (e) {
237                 child._value = e;
238                 child.next();
239               }
240             );
241           }else {
242             onRejection(exception).then(
243               () {
244                 child._isPending = false;
245                 child.next();
246               },
247               (e) {
248                 child._value = e;
249                 child.next();
250               }
251             );
252           }
253         }
254       }
255 
256       try {
257         this._value.tryVisit!(
258           (Exception e) => reject(e),
259           () => fulfill()
260         );
261       }catch(Exception e) {
262         try{
263           child._value = e;
264         }catch(Exception e){} //例外は発生しない
265         child.next();
266       }
267     };
268 
269     if(!this.isPending) this.next();
270 
271     return child;
272   }
273 }