update to PHP-8.1
[m6w6/seekat] / lib / API / Consumer.php
1 <?php
2
3 namespace seekat\API;
4
5 use Exception;
6 use Generator;
7 use seekat\API;
8 use seekat\Exception\{function exception};
9
10 final class Consumer {
11 /**
12 * The return value of the generator
13 * @var mixed
14 */
15 private mixed $result = null;
16
17 /**
18 * Cancellation flag
19 */
20 private bool $cancelled = false;
21
22 /**
23 * Promise
24 */
25 private object $promise;
26
27 private \Closure $resolve;
28 private \Closure $reject;
29 private \Closure $reduce;
30
31 /**
32 * Create a new generator consumer
33 */
34 function __construct(private readonly Future $future, private readonly \Closure $loop) {
35 $context = $future->createContext(function() {
36 $this->cancelled = true;
37 });
38 $this->promise = $future->getPromise($context);
39 $this->resolve = $future->resolver($context);
40 $this->reject = $future->rejecter($context);
41 $this->reduce = $future->reducer();
42 }
43
44 /**
45 * Iterate over $gen, a \Generator yielding promises
46 *
47 * @param Generator $gen
48 * @return mixed promise
49 */
50 function __invoke(Generator $gen) : mixed {
51 $this->cancelled = false;
52 foreach ($gen as $promise) {
53 if ($this->cancelled) {
54 break;
55 }
56 $this->give($promise, $gen);
57 }
58
59 if ($this->cancelled) {
60 ($this->reject)("Cancelled");
61 } else if (!$gen->valid()) {
62 try {
63 $this->result = $gen->getReturn();
64 } catch (Exception $e) {
65 assert($e->getMessage() === "Cannot get return value of a generator that hasn't returned");
66 }
67 }
68 ($this->resolve)($this->result);
69
70 return $this->promise;
71 }
72
73 /**
74 * Promise handler
75 *
76 * @param mixed $promise
77 * @param Generator $gen
78 */
79 private function give(mixed $promise, Generator $gen) : void {
80 if ($promise instanceof \Traversable) {
81 $promise = iterator_to_array($promise);
82 }
83 if (is_array($promise)) {
84 $promise = ($this->reduce)($promise);
85 }
86
87 $this->future->handlePromise($promise, function($result) use($gen) {
88 if (($promise = $gen->send($result))) {
89 $this->give($promise, $gen);
90 }
91 }, function($error) use($gen) {
92 $gen->throw(exception($error));
93 });
94
95 /* FIXME: external loop */
96 ($this->loop)();
97 }
98 }