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