1 module sbylib.engine.promise;
2 
3 import std;
4 import core.thread;
5 import sbylib.event;
6 
7 class Promise(T) {
8 
9     alias Type = T;
10 
11     private VoidEvent task;
12     private Exception e;
13 
14     static if (!is(T == void)) {
15         private Nullable!T result;
16     }
17 
18     static if (is(T == void)) {
19         this(void delegate(void delegate()) f) {
20             this.task = when(Frame).then({});
21             f({ this.task.kill(); });
22         }
23     } else {
24         this(void delegate(void delegate(T)) f) {
25             this.task = when(Frame).then({});
26             f((T t) {
27                 result = t.nullable;
28                 // writeln("result = ", t);
29                 this.task.kill();
30             });
31         }
32     }
33 
34     this(Args...)(T delegate(Args) f, Args args) {
35         this.task = when(Frame).once({
36             this.exec(f, args);
37         });
38     }
39 
40     this(Args...)(T delegate(Args) f, Promise!Args parent) {
41         this.task = when(Frame).then({
42             // writeln("po");
43             if (parent.finished is false) return;
44             static if (is(P.Type == void)) {
45                 this.exec(f);
46             } else {
47                 // for avoid execution on parent's error
48                 if (parent.result.isNull is false) {
49                     this.exec(f, parent.result.get());
50                 }
51             }
52             this.task.kill();
53         });
54     }
55 
56     this(Args...)(Promise!T delegate(Args) f, Promise!Args parent) {
57         this.task = when(Frame).then({
58             if (parent.finished is false) return;
59             static if (is(P.Ret == void)) {
60                 this.exec(f);
61             } else {
62                 this.exec(f, parent.result.get());
63             }
64         });
65     }
66 
67     private void exec(Args...)(T delegate(Args) f, Args args) {
68         execWithErrorHandling({
69             static if (is(T == void)) {
70                 f(args);
71             } else {
72                 result = f(args).nullable;
73             }
74         });
75     }
76 
77     private void exec(Args...)(Promise!T delegate(Args) f, Args args) {
78         execWithErrorHandling({
79             static if (is(T == void)) {
80                 f(args).then({
81                     this.task.kill();
82                 });
83             } else {
84                 f(args).then((r) {
85                     result = r.nullable;
86                     this.task.kill();
87                 });
88             }
89         });
90     }
91 
92     private void execWithErrorHandling(void delegate() func) {
93         try {
94             func();
95         } catch (Exception e) {
96             this.e = e;
97         }
98     }
99 
100     auto then(F)(F f) {
101         static if (isInstanceOf!(Promise, ReturnType!F)) {
102             static if (is(F == delegate)) {
103                 return new Promise!(ReturnType!(F).Type)(f, this);
104             } else {
105                 return new Promise!(ReturnType!(F).Type)(f.toDelegate(), this);
106             }
107         } else {
108             static if (is(F == delegate)) {
109                 return new Promise!(ReturnType!F)(f, this);
110             } else {
111                 return new Promise!(ReturnType!F)(f.toDelegate(), this);
112             }
113         }
114     }
115 
116     auto error(Ex)(void delegate(Ex) f) {
117         when(this.e !is null).once({
118             f(cast(Ex)this.e);
119         });
120         return this;
121     }
122 
123     bool finished() 
124     {
125         return this.task.isAlive is false;
126     }
127 }
128 
129 auto promise(alias f)(Parameters!f args) {
130     static if (is (f == delegate)) {
131         return new Promise!(ReturnType!f)(f, args);
132     } else {
133         return new Promise!(ReturnType!f)(f.toDelegate(), args);
134     }
135 }
136 
137 auto promise(alias f)() if (is(Parameters!f[0] == delegate)) {
138     static if (is (f == delegate)) {
139         return new Promise!(Parameters!(Parameters!f[0])[0])(f);
140     } else {
141         return new Promise!(Parameters!(Parameters!f[0])[0])(f.toDelegate());
142     }
143 }