Completed
Push — master ( 33b2da...23d19d )
by Harry
01:24
created

Table::getSummary()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 17
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 17
c 0
b 0
f 0
rs 9.4285
cc 3
eloc 12
nc 3
nop 0
1
<?php
2
/**
3
 * This file is part of graze/parallel-process.
4
 *
5
 * Copyright (c) 2017 Nature Delivered Ltd. <https://www.graze.com>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 *
10
 * @license https://github.com/graze/parallel-process/blob/master/LICENSE.md
11
 * @link    https://github.com/graze/parallel-process
12
 */
13
14
namespace Graze\ParallelProcess;
15
16
use Exception;
17
use Graze\DiffRenderer\DiffConsoleOutput;
18
use Graze\DiffRenderer\Terminal\TerminalInterface;
19
use Symfony\Component\Console\Output\OutputInterface;
20
use Symfony\Component\Process\Exception\ProcessFailedException;
21
use Symfony\Component\Process\Process;
22
23
class Table
24
{
25
    const SPINNER = "⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏";
26
27
    /** @var Pool */
28
    private $processPool;
29
    /** @var string[] */
30
    private $rows = [];
31
    /** @var Exception[] */
32
    private $exceptions;
33
    /** @var int[] */
34
    private $maxLengths = [];
35
    /** @var DiffConsoleOutput */
36
    private $output;
37
    /** @var TerminalInterface */
38
    private $terminal;
39
    /** @var bool */
40
    private $showOutput = true;
41
    /** @var bool */
42
    private $showSummary = true;
43
44
    /**
45
     * Table constructor.
46
     *
47
     * @param OutputInterface $output
48
     * @param Pool|null       $pool
49
     */
50
    public function __construct(OutputInterface $output, Pool $pool = null)
51
    {
52
        $this->processPool = $pool ?: new Pool();
53
        if (!$output instanceof DiffConsoleOutput) {
54
            $this->output = new DiffConsoleOutput($output);
55
            $this->output->setTrim(true);
56
        } else {
57
            $this->output = $output;
58
        }
59
        $this->terminal = $this->output->getTerminal();
60
        $this->exceptions = [];
61
    }
62
63
    /**
64
     * Parses the rows to determine the key lengths to make a pretty table
65
     *
66
     * @param array $data
67
     */
68
    private function updateRowKeyLengths(array $data = [])
69
    {
70
        $lengths = array_map('mb_strlen', $data);
71
72
        $keys = array_merge(array_keys($lengths), array_keys($this->maxLengths));
73
74
        foreach ($keys as $key) {
75
            if (!isset($this->maxLengths[$key])
76
                || (isset($lengths[$key]) && $lengths[$key] > $this->maxLengths[$key])
77
            ) {
78
                $this->maxLengths[$key] = $lengths[$key];
79
            }
80
        }
81
    }
82
83
    /**
84
     * @param array  $data
85
     * @param string $status
86
     * @param float  $duration
87
     * @param string $extra
88
     *
89
     * @return string
90
     */
91
    private function formatRow(array $data, $status, $duration, $extra = '')
92
    {
93
        $info = [];
94
        foreach ($data as $key => $value) {
95
            $length = isset($this->maxLengths[$key]) ? '-' . $this->maxLengths[$key] : '';
96
            $info[] = sprintf("<info>%s</info>: %{$length}s", $key, $value);
97
        }
98
        $extra = $extra ? '  ' . $this->terminal->filter($extra) : '';
99
        return sprintf("%s (<comment>%6.2fs</comment>) %s%s", implode(' ', $info), $duration, $status, $extra);
100
    }
101
102
    /**
103
     * @param Process $process
104
     * @param array   $data
105
     */
106
    public function add(Process $process, array $data = [])
107
    {
108
        $index = count($this->rows);
109
        $this->rows[$index] = $this->formatRow($data, '', 0);
110
        $spinner = 0;
111
112
        if ($this->output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) {
113
            $onProgress = function ($process, $duration, $last) use ($index, $data, &$spinner) {
114
                $this->rows[$index] = $this->formatRow(
115
                    $data,
116
                    mb_substr(static::SPINNER, $spinner++, 1),
117
                    $duration,
118
                    ($this->showOutput ? $last : '')
119
                );
120
                if ($spinner > mb_strlen(static::SPINNER) - 1) {
121
                    $spinner = 0;
122
                }
123
                $this->render();
124
            };
125
        } else {
126
            $onProgress = null;
127
        }
128
129
        $this->processPool->add(new Run(
130
            $process,
131
            function ($process, $duration, $last) use ($index, $data) {
132
                $this->rows[$index] = $this->formatRow($data, "<info>✓</info>", $duration, $last);
133
                $this->render($index);
134
            },
135
            function ($process, $duration, $last) use ($index, $data) {
136
                $this->rows[$index] = $this->formatRow($data, "<error>x</error>", $duration, $last);
137
                $this->render($index);
138
                $this->exceptions[] = new ProcessFailedException($process);
139
            },
140
            $onProgress
141
        ));
142
143
        $this->updateRowKeyLengths($data);
144
    }
145
146
    /**
147
     * @return string
148
     */
149
    private function getSummary()
150
    {
151
        if ($this->processPool->hasStarted()) {
152
            if ($this->processPool->isRunning()) {
153
                return sprintf(
154
                    '<comment>Total</comment>: %2d, <comment>Running</comment>: %2d, <comment>Waiting</comment>: %2d',
155
                    $this->processPool->count(),
156
                    count($this->processPool->getRunning()),
157
                    count($this->processPool->getWaiting())
158
                );
159
            } else {
160
                return '';
161
            }
162
        } else {
163
            return 'waiting...';
164
        }
165
    }
166
167
    /**
168
     * Render a specific row
169
     *
170
     * @param int $row
171
     */
172
    private function render($row = 0)
173
    {
174
        if ($this->output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) {
175
            $rows = ($this->showSummary ? array_merge($this->rows, [$this->getSummary()]) : $this->rows);
176
            $this->output->reWrite($rows, !$this->showSummary);
177
        } else {
178
            $this->output->writeln($this->rows[$row]);
179
        }
180
    }
181
182
    /**
183
     * @param float $checkInterval
184
     *
185
     * @return bool true if all processes were successful
186
     * @throws Exception
187
     */
188
    public function run($checkInterval = Pool::CHECK_INTERVAL)
189
    {
190
        if ($this->output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) {
191
            $this->render();
192
        }
193
        $output = $this->processPool->run($checkInterval);
194
        if ($this->output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE && $this->showSummary) {
195
            $this->render();
196
        }
197
198
        if (count($this->exceptions) > 0) {
199
            foreach ($this->exceptions as $exception) {
200
                $this->output->writeln($exception->getMessage());
201
            }
202
203
            throw reset($this->exceptions);
204
        }
205
206
        return $output;
207
    }
208
209
    /**
210
     * @return bool
211
     */
212
    public function isShowOutput()
213
    {
214
        return $this->showOutput;
215
    }
216
217
    /**
218
     * @param bool $showOutput
219
     *
220
     * @return $this
221
     */
222
    public function setShowOutput($showOutput)
223
    {
224
        $this->showOutput = $showOutput;
225
        return $this;
226
    }
227
228
    /**
229
     * @return bool
230
     */
231
    public function isShowSummary()
232
    {
233
        return $this->showSummary;
234
    }
235
236
    /**
237
     * @param bool $showSummary
238
     *
239
     * @return $this
240
     */
241
    public function setShowSummary($showSummary)
242
    {
243
        $this->showSummary = $showSummary;
244
        return $this;
245
    }
246
}
247