1 /*
2  * Copyright (c) 2011-2017 Contributors to the Eclipse Foundation
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License 2.0 which is available at
6  * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7  * which is available at https://www.apache.org/licenses/LICENSE-2.0.
8  *
9  * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10  */
11 module hunt.database.base.AsyncResult;
12 
13 import hunt.Exceptions;
14 import hunt.Functions;
15 import hunt.Object;
16 
17 alias AsyncResultHandler(T) = Action1!(AsyncResult!T);
18 alias AsyncVoidHandler = AsyncResultHandler!Void;
19 alias VoidAsyncResult = AsyncResult!Void;
20 
21 interface IAsyncResult {
22 
23     /**
24      * A Throwable describing failure. This will be null if the operation succeeded.
25      *
26      * @return the cause or null if the operation succeeded.
27      */
28     Throwable cause();
29 
30     /**
31      * Did it succeed?
32      *
33      * @return true if it succeded or false otherwise
34      */
35     bool succeeded();
36 
37     /**
38      * Did it fail?
39      *
40      * @return true if it failed or false otherwise
41      */
42     bool failed();
43 
44 }
45 
46 /**
47  * Encapsulates the result of an asynchronous operation.
48  * <p>
49  * Many operations in Vert.x APIs provide results back by passing an instance of this in a {@link hunt.net.Handler}.
50  * <p>
51  * The result can either have failed or succeeded.
52  * <p>
53  * If it failed then the cause of the failure is available with {@link #cause}.
54  * <p>
55  * If it succeeded then the actual result is available with {@link #result}
56  *
57  * @author <a href="http://tfox.org">Tim Fox</a>
58  */
59 interface AsyncResult(T) : IAsyncResult {
60 
61     /**
62      * The result of the operation. This will be null if the operation failed.
63      *
64      * @return the result or null if the operation failed.
65      */
66     T result();
67 
68     /**
69      * Apply a {@code mapper} function on this async result.<p>
70      *
71      * The {@code mapper} is called with the completed value and this mapper returns a value. This value will complete the result returned by this method call.<p>
72      *
73      * When this async result is failed, the failure will be propagated to the returned async result and the {@code mapper} will not be called.
74      *
75      * @param mapper the mapper function
76      * @return the mapped async result
77      */
78     final AsyncResult!(U) map(U)(Function!(T, U) mapper) {
79         if (mapper is null) {
80             throw new NullPointerException();
81         }
82         return new class AsyncResult!(U) {
83             override U result() {
84                 if (succeeded()) {
85                     return mapper(this.outer.result());
86                 } else {
87                     return null;
88                 }
89             }
90 
91             override Throwable cause() {
92                 return this.outer.cause();
93             }
94 
95             override bool succeeded() {
96                 return this.outer.succeeded();
97             }
98 
99             override bool failed() {
100                 return this.outer.failed();
101             }
102         };
103     }
104 
105     /**
106      * Map the result of this async result to a specific {@code value}.<p>
107      *
108      * When this async result succeeds, this {@code value} will succeeed the async result returned by this method call.<p>
109      *
110      * When this async result fails, the failure will be propagated to the returned async result.
111      *
112      * @param value the value that eventually completes the mapped async result
113      * @return the mapped async result
114      */
115     final AsyncResult!(V) map(V)(V value) {
116         return map!(V)((t) => value);
117     }
118 
119     /**
120      * Map the result of this async result to {@code null}.<p>
121      *
122      * This is a convenience for {@code asyncResult.map((T) null)} or {@code asyncResult.map((Void) null)}.<p>
123      *
124      * When this async result succeeds, {@code null} will succeeed the async result returned by this method call.<p>
125      *
126      * When this async result fails, the failure will be propagated to the returned async result.
127      *
128      * @return the mapped async result
129      */
130     final AsyncResult!(V) mapEmpty(V)() {
131         return map!(V)(V.init);
132     }
133 
134     /**
135      * Apply a {@code mapper} function on this async result.<p>
136      *
137      * The {@code mapper} is called with the failure and this mapper returns a value. This value will complete the result returned by this method call.<p>
138      *
139      * When this async result is succeeded, the value will be propagated to the returned async result and the {@code mapper} will not be called.
140      *
141      * @param mapper the mapper function
142      * @return the mapped async result
143      */
144     final AsyncResult!(T) otherwise(Function!(Throwable, T) mapper) {
145         if (mapper is null) {
146             throw new NullPointerException();
147         }
148         return new class AsyncResult!(T) {
149             override T result() {
150                 if (this.outer.succeeded()) {
151                     return this.outer.result();
152                 } else if (this.outer.failed()) {
153                     return mapper(this.outer.cause());
154                 } else {
155                     static if(is(T == class) || is(T == interface)) {
156                         return null;
157                     } else {
158                         return T.init;
159                     }
160                 }
161             }
162 
163             override Throwable cause() {
164                 return null;
165             }
166 
167             override bool succeeded() {
168                 return this.outer.succeeded() || this.outer.failed();
169             }
170 
171             override bool failed() {
172                 return false;
173             }
174         };
175     }
176 
177     /**
178      * Map the failure of this async result to a specific {@code value}.<p>
179      *
180      * When this async result fails, this {@code value} will succeeed the async result returned by this method call.<p>
181      *
182      * When this async succeeds, the result will be propagated to the returned async result.
183      *
184      * @param value the value that eventually completes the mapped async result
185      * @return the mapped async result
186      */
187     final AsyncResult!(T) otherwise(T value) {
188         return otherwise((err) => value);
189     }
190 
191     /**
192      * Map the failure of this async result to {@code null}.<p>
193      *
194      * This is a convenience for {@code asyncResult.otherwise((T) null)}.<p>
195      *
196      * When this async result fails, the {@code null} will succeeed the async result returned by this method call.<p>
197      *
198      * When this async succeeds, the result will be propagated to the returned async result.
199      *
200      * @return the mapped async result
201      */
202     final AsyncResult!(T) otherwiseEmpty() {
203         return otherwise((err) => T.init);
204     }
205 
206 }
207 
208 AsyncResult!T succeededResult(T)(T v) {
209     return new class AsyncResult!(T) {
210         override T result() {
211             return v;
212         }
213 
214         override Throwable cause() {
215             return null;
216         }
217 
218         override bool succeeded() {
219             return true;
220         }
221 
222         override bool failed() {
223             return false;
224         }
225     };
226 }
227 
228 
229 AsyncResult!T failedResult(T)(Throwable t) {
230     return new class AsyncResult!(T) {
231         override T result() {
232             static if(is(T == class) || is(T == interface)) {
233                 return null;
234             } else {
235                 return T.init;
236             }
237         }
238 
239         override Throwable cause() {
240             return t;
241         }
242 
243         override bool succeeded() {
244             return false;
245         }
246 
247         override bool failed() {
248             return true;
249         }
250     };
251 }