mod_s2s: Remove warning about hostname mismatch
[prosody.git] / util / promise.lua
blob0b182b54c0ecb86c9fdecfc45ecf305661401877
1 local promise_methods = {};
2 local promise_mt = { __name = "promise", __index = promise_methods };
4 local xpcall = require "util.xpcall".xpcall;
6 function promise_mt:__tostring()
7 return "promise (" .. (self._state or "invalid") .. ")";
8 end
10 local function is_promise(o)
11 local mt = getmetatable(o);
12 return mt == promise_mt;
13 end
15 local function wrap_handler(f, resolve, reject, default)
16 if not f then
17 return default;
18 end
19 return function (param)
20 local ok, ret = xpcall(f, debug.traceback, param);
21 if ok then
22 resolve(ret);
23 else
24 reject(ret);
25 end
26 return true;
27 end;
28 end
30 local function next_pending(self, on_fulfilled, on_rejected, resolve, reject)
31 table.insert(self._pending_on_fulfilled, wrap_handler(on_fulfilled, resolve, reject, resolve));
32 table.insert(self._pending_on_rejected, wrap_handler(on_rejected, resolve, reject, reject));
33 end
35 local function next_fulfilled(promise, on_fulfilled, on_rejected, resolve, reject) -- luacheck: ignore 212/on_rejected
36 wrap_handler(on_fulfilled, resolve, reject, resolve)(promise.value);
37 end
39 local function next_rejected(promise, on_fulfilled, on_rejected, resolve, reject) -- luacheck: ignore 212/on_fulfilled
40 wrap_handler(on_rejected, resolve, reject, reject)(promise.reason);
41 end
43 local function promise_settle(promise, new_state, new_next, cbs, value)
44 if promise._state ~= "pending" then
45 return;
46 end
47 promise._state = new_state;
48 promise._next = new_next;
49 for _, cb in ipairs(cbs) do
50 cb(value);
51 end
52 -- No need to keep references to callbacks
53 promise._pending_on_fulfilled = nil;
54 promise._pending_on_rejected = nil;
55 return true;
56 end
58 local function new_resolve_functions(p)
59 local resolved = false;
60 local function _resolve(v)
61 if resolved then return; end
62 resolved = true;
63 if is_promise(v) then
64 v:next(new_resolve_functions(p));
65 elseif promise_settle(p, "fulfilled", next_fulfilled, p._pending_on_fulfilled, v) then
66 p.value = v;
67 end
69 end
70 local function _reject(e)
71 if resolved then return; end
72 resolved = true;
73 if promise_settle(p, "rejected", next_rejected, p._pending_on_rejected, e) then
74 p.reason = e;
75 end
76 end
77 return _resolve, _reject;
78 end
80 local function new(f)
81 local p = setmetatable({ _state = "pending", _next = next_pending, _pending_on_fulfilled = {}, _pending_on_rejected = {} }, promise_mt);
82 if f then
83 local resolve, reject = new_resolve_functions(p);
84 local ok, ret = pcall(f, resolve, reject);
85 if not ok and p._state == "pending" then
86 reject(ret);
87 end
88 end
89 return p;
90 end
92 local function all(promises)
93 return new(function (resolve, reject)
94 local count, total, results = 0, #promises, {};
95 for i = 1, total do
96 promises[i]:next(function (v)
97 results[i] = v;
98 count = count + 1;
99 if count == total then
100 resolve(results);
102 end, reject);
104 end);
107 local function race(promises)
108 return new(function (resolve, reject)
109 for i = 1, #promises do
110 promises[i]:next(resolve, reject);
112 end);
115 local function resolve(v)
116 return new(function (_resolve)
117 _resolve(v);
118 end);
121 local function reject(v)
122 return new(function (_, _reject)
123 _reject(v);
124 end);
127 local function try(f)
128 return resolve():next(function () return f(); end);
131 function promise_methods:next(on_fulfilled, on_rejected)
132 return new(function (resolve, reject) --luacheck: ignore 431/resolve 431/reject
133 self:_next(on_fulfilled, on_rejected, resolve, reject);
134 end);
137 function promise_methods:catch(on_rejected)
138 return self:next(nil, on_rejected);
141 function promise_methods:finally(on_finally)
142 local function _on_finally(value) on_finally(); return value; end
143 local function _on_catch_finally(err) on_finally(); return reject(err); end
144 return self:next(_on_finally, _on_catch_finally);
147 return {
148 new = new;
149 resolve = resolve;
150 reject = reject;
151 all = all;
152 race = race;
153 try = try;
154 is_promise = is_promise;