Passed
Push — master ( eca042...4a0c97 )
by Alec
03:22 queued 13s
created

Spinner::begin()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 5
dl 0
loc 8
ccs 6
cts 6
cp 1
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 1
crap 2
1
<?php declare(strict_types=1);
2
3
namespace AlecRabbit\Spinner\Core;
4
5
use AlecRabbit\Accessories\Circular;
6
use AlecRabbit\Accessories\Pretty;
7
use AlecRabbit\Cli\Tools\Cursor;
8
use AlecRabbit\Spinner\Core\Adapters\EchoOutputAdapter;
9
use AlecRabbit\Spinner\Core\Contracts\SettingsInterface;
10
use AlecRabbit\Spinner\Core\Contracts\SpinnerInterface;
11
use AlecRabbit\Spinner\Core\Contracts\SpinnerOutputInterface;
12
use AlecRabbit\Spinner\Core\Contracts\SpinnerSymbols;
13
use function AlecRabbit\typeOf;
14
use const AlecRabbit\ESC;
15
16
abstract class Spinner implements SpinnerInterface
17
{
18
    protected const ERASING_SHIFT = SettingsInterface::DEFAULT_ERASING_SHIFT;
19
    protected const INTERVAL = SettingsInterface::DEFAULT_INTERVAL;
20
    protected const SYMBOLS = SpinnerSymbols::DIAMOND;
21
    protected const STYLES = [];
22
23
    /** @var string */
24
    protected $messageStr;
25
    /** @var string */
26
    protected $percentStr = '';
27
    /** @var string */
28
    protected $percentPrefix;
29
    /** @var string */
30
    protected $moveBackSequenceStr;
31
    /** @var string */
32
    protected $inlinePaddingStr;
33
    /** @var string */
34
    protected $eraseBySpacesStr;
35
    /** @var Style */
36
    protected $style;
37
    /** @var float */
38
    protected $interval;
39
    /** @var int */
40
    protected $erasingShift;
41
    /** @var Circular */
42
    protected $symbols;
43
    /** @var null|SpinnerOutputInterface */
44
    protected $output;
45
46
    /**
47
     * AbstractSpinner constructor.
48
     * @param mixed $settings
49
     * @param null|false|SpinnerOutputInterface $output
50
     * @param mixed $color
51
     */
52 21
    public function __construct($settings = null, $output = null, $color = null)
53
    {
54 21
        $this->output = $this->refineOutput($output);
55 19
        $settings = $this->refineSettings($settings);
56 18
        $this->interval = $settings->getInterval();
57 18
        $this->erasingShift = $settings->getErasingShift();
58 18
        $this->inlinePaddingStr = $settings->getInlinePaddingStr();
59 18
        $this->messageStr = $this->getMessageStr($settings);
60 18
        $this->setFields();
61 18
        $this->symbols = new Circular($settings->getSymbols());
62
        try {
63 18
            $this->style = new Style($settings->getStyles(), $color);
64 1
        } catch (\Throwable $e) {
65 1
            throw new \InvalidArgumentException(
66 1
                '[' . static::class . '] ' . $e->getMessage(),
67 1
                (int)$e->getCode(),
68 1
                $e
69
            );
70
        }
71 17
    }
72
73
    /**
74
     * @param null|false|SpinnerOutputInterface $output
75
     * @return null|SpinnerOutputInterface
76
     */
77 21
    protected function refineOutput($output): ?SpinnerOutputInterface
78
    {
79 21
        $this->assertOutput($output);
80 19
        if (false === $output) {
81 11
            return null;
82
        }
83 8
        return $output ?? new EchoOutputAdapter();
84
    }
85
86
    /**
87
     * @param mixed $output
88
     */
89 21
    protected function assertOutput($output): void
90
    {
91 21
        if (null !== $output && false !== $output && !$output instanceof SpinnerOutputInterface) {
92 2
            $type = true === $output ? 'true' : typeOf($output);
93 2
            throw new \InvalidArgumentException(
94 2
                'Incorrect $output param [null|false|SpinnerOutputInterface] expected "' . $type . '" given.'
95
            );
96
        }
97 19
    }
98
99
    /**
100
     * @param mixed $settings
101
     * @return SettingsInterface
102
     */
103 19
    protected function refineSettings($settings): SettingsInterface
104
    {
105 19
        $this->assertSettings($settings);
106 18
        if (\is_string($settings)) {
107
            return
108 13
                $this->defaultSettings()->setMessage($settings);
109
        }
110
        return
111 5
            $settings ?? $this->defaultSettings();
112
    }
113
114
    /**
115
     * @param mixed $settings
116
     */
117 19
    protected function assertSettings($settings): void
118
    {
119 19
        if (null !== $settings && !\is_string($settings) && !$settings instanceof SettingsInterface) {
120 1
            throw new \InvalidArgumentException(
121 1
                'Instance of SettingsInterface or string expected ' . typeOf($settings) . ' given.'
122
            );
123
        }
124 18
    }
125
126
    /**
127
     * @return SettingsInterface
128
     */
129 15
    protected function defaultSettings(): SettingsInterface
130
    {
131
        return
132 15
            (new Settings())
133 15
                ->setInterval(static::INTERVAL)
134 15
                ->setErasingShift(static::ERASING_SHIFT)
135 15
                ->setSymbols(static::SYMBOLS)
136 15
                ->setStyles(static::STYLES);
137
    }
138
139
    /**
140
     * @param SettingsInterface $settings
141
     * @return string
142
     */
143 18
    protected function getMessageStr(SettingsInterface $settings): string
144
    {
145 18
        return $settings->getPrefix() . ucfirst($settings->getMessage()) . $settings->getSuffix();
146
    }
147
148 18
    protected function setFields(): void
149
    {
150 18
        $this->percentPrefix = $this->getPercentPrefix();
151
        $strLen =
152 18
            strlen($this->message()) + strlen($this->percent()) + strlen($this->inlinePaddingStr) + $this->erasingShift;
153 18
        $this->moveBackSequenceStr = ESC . "[{$strLen}D";
154 18
        $this->eraseBySpacesStr = str_repeat(SettingsInterface::ONE_SPACE_SYMBOL, $strLen);
155 18
    }
156
157
    /**
158
     * @return string
159
     */
160 18
    protected function getPercentPrefix(): string
161
    {
162 18
        if (strpos($this->messageStr, SettingsInterface::DEFAULT_SUFFIX)) {
163 15
            return SettingsInterface::ONE_SPACE_SYMBOL;
164
        }
165 3
        return SettingsInterface::EMPTY;
166
    }
167
168
    /**
169
     * @return string
170
     */
171 18
    protected function message(): string
172
    {
173 18
        return $this->messageStr;
174
    }
175
176
    /**
177
     * @return string
178
     */
179 18
    protected function percent(): string
180
    {
181 18
        return $this->percentStr;
182
    }
183
184
    /** {@inheritDoc} */
185 2
    public function getOutput(): ?SpinnerOutputInterface
186
    {
187 2
        return $this->output;
188
    }
189
190 2
    public function interval(): float
191
    {
192 2
        return $this->interval;
193
    }
194
195 4
    public function inline(bool $inline): SpinnerInterface
196
    {
197 4
        $this->inlinePaddingStr = $inline ? SettingsInterface::ONE_SPACE_SYMBOL : SettingsInterface::EMPTY;
198 4
        $this->setFields();
199 4
        return $this;
200
    }
201
202
    /** {@inheritDoc} */
203 14
    public function begin(?float $percent = null): string
204
    {
205 14
        if ($this->output) {
206 3
            $this->output->write(Cursor::hide());
207 3
            $this->spin($percent);
208 3
            return '';
209
        }
210 11
        return Cursor::hide() . $this->spin($percent);
211
    }
212
213
    /** {@inheritDoc} */
214 14
    public function spin(?float $percent = null): string
215
    {
216 14
        if (null !== $percent) {
217 4
            $this->updatePercent($percent);
218
        }
219 14
        $str = $this->inlinePaddingStr .
220 14
            $this->style->spinner((string)$this->symbols->value()) .
221 14
            $this->style->message(
222 14
                $this->message()
223
            ) .
224 14
            $this->style->percent(
225 14
                $this->percent()
226
            ) .
227 14
            $this->moveBackSequenceStr;
228 14
        if ($this->output) {
229 3
            $this->output->write($str);
230 3
            return '';
231
        }
232
        return
233 11
            $str;
234
    }
235
236
    /**
237
     * @param float $percent
238
     */
239 4
    protected function updatePercent(float $percent): void
240
    {
241 4
        if (0 === (int)($percent * 1000) % 10) {
242 4
            $this->percentStr = Pretty::percent($percent, 0, $this->percentPrefix);
243 4
            $this->setFields();
244
        }
245 4
    }
246
247
    /** {@inheritDoc} */
248 14
    public function end(): string
249
    {
250 14
        if ($this->output) {
251 3
            $this->erase();
252 3
            $this->output->write(Cursor::show());
253 3
            return '';
254
        }
255 11
        return $this->erase() . Cursor::show();
256
    }
257
258
    /** {@inheritDoc} */
259 14
    public function erase(): string
260
    {
261 14
        $str = $this->eraseBySpacesStr . $this->moveBackSequenceStr;
262 14
        if ($this->output) {
263 3
            $this->output->write($str);
264 3
            return '';
265
        }
266 11
        return $str;
267
    }
268
}
269