Passed
Push — master ( 285a03...55e432 )
by Alec
02:42
created

Spinner::getSettings()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 2
c 1
b 0
f 0
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 1
nc 1
nop 0
crap 1
1
<?php declare(strict_types=1);
2
3
namespace AlecRabbit\Spinner\Core;
4
5
use AlecRabbit\Accessories\Circular;
6
use AlecRabbit\Cli\Tools\Cursor;
7
use AlecRabbit\Spinner\Core\Adapters\EchoOutputAdapter;
8
use AlecRabbit\Spinner\Core\Contracts\Frames;
9
use AlecRabbit\Spinner\Core\Contracts\SettingsInterface;
10
use AlecRabbit\Spinner\Core\Contracts\SpinnerInterface;
11
use AlecRabbit\Spinner\Core\Contracts\SpinnerOutputInterface;
12
use function AlecRabbit\typeOf;
13
use const AlecRabbit\ESC;
14
15
abstract class Spinner implements SpinnerInterface
16
{
17
    protected const INTERVAL = SettingsInterface::DEFAULT_INTERVAL;
18
    protected const FRAMES = Frames::DIAMOND;
19
    protected const STYLES = [];
20
21
    /** @var string */
22
    protected $messageStr;
23
    /** @var string */
24
    protected $currentMessage;
25
    /** @var string */
26
    protected $currentMessagePrefix;
27
    /** @var string */
28
    protected $currentMessageSuffix;
29
    /** @var string */
30
    protected $percentStr = '';
31
    /** @var int */
32
    protected $percentStrLen = 0;
33
    /** @var string */
34
    protected $percentSpacer;
35
    /** @var string */
36
    protected $moveBackSequenceStr;
37
    /** @var string */
38
    protected $inlinePaddingStr;
39
    /** @var string */
40
    protected $eraseBySpacesStr;
41
    /** @var Style */
42
    protected $style;
43
    /** @var float */
44
    protected $interval;
45
    /** @var int */
46
    protected $frameErasingShift;
47
    /** @var Circular */
48
    protected $symbols;
49
    /** @var null|SpinnerOutputInterface */
50
    protected $output;
51
    /** @var int */
52
    protected $messageErasingLen;
53
    /** @var string */
54
    protected $spacer;
55
    /** @var SettingsInterface */
56
    protected $settings;
57
    /** @var int */
58
    protected $currentMessagePrefixLen;
59
    /** @var int */
60
    protected $currentMessageSuffixLen;
61
    /** @var int */
62
    protected $inlinePaddingStrLen;
63
64
    /**
65
     * AbstractSpinner constructor.
66
     *
67
     * @param mixed $settings
68
     * @param null|false|SpinnerOutputInterface $output
69
     * @param mixed $color
70
     */
71 26
    public function __construct($settings = null, $output = null, $color = null)
72
    {
73 26
        $this->output = $this->refineOutput($output);
74 24
        $this->settings = $this->refineSettings($settings);
75 23
        $this->loadSettings($color);
76 22
    }
77
78
    /**
79
     * @param null|false|SpinnerOutputInterface $output
80
     * @return null|SpinnerOutputInterface
81
     */
82 26
    protected function refineOutput($output): ?SpinnerOutputInterface
83
    {
84 26
        $this->assertOutput($output);
85 24
        if (false === $output) {
86 15
            return null;
87
        }
88 9
        return $output ?? new EchoOutputAdapter();
89
    }
90
91
    /**
92
     * @param mixed $output
93
     */
94 26
    protected function assertOutput($output): void
95
    {
96 26
        if (null !== $output && false !== $output && !$output instanceof SpinnerOutputInterface) {
97 2
            $typeOrValue = true === $output ? 'true' : typeOf($output);
98 2
            throw new \InvalidArgumentException(
99
                'Incorrect $output param' .
100
                ' [null|false|SpinnerOutputInterface] expected'
101 2
                . ' "' . $typeOrValue . '" given.'
102
            );
103
        }
104 24
    }
105
106
    /**
107
     * @param mixed $settings
108
     * @return SettingsInterface
109
     */
110 24
    protected function refineSettings($settings): SettingsInterface
111
    {
112 24
        $this->assertSettings($settings);
113 23
        if (\is_string($settings)) {
114
            return
115 17
                $this->defaultSettings()->setMessage($settings);
116
        }
117
        return
118 6
            $settings ?? $this->defaultSettings();
119
    }
120
121
    /**
122
     * @param mixed $settings
123
     */
124 24
    protected function assertSettings($settings): void
125
    {
126 24
        if (null !== $settings && !\is_string($settings) && !$settings instanceof SettingsInterface) {
127 1
            throw new \InvalidArgumentException(
128 1
                'Instance of SettingsInterface or string expected ' . typeOf($settings) . ' given.'
129
            );
130
        }
131 23
    }
132
133
    /**
134
     * @return SettingsInterface
135
     */
136 19
    protected function defaultSettings(): SettingsInterface
137
    {
138
        return
139 19
            (new Settings())
140 19
                ->setInterval(static::INTERVAL)
141 19
                ->setSymbols(static::FRAMES)
142 19
                ->setStyles(static::STYLES);
143
    }
144
145
    /**
146
     * @param mixed $color
147
     */
148 23
    protected function loadSettings($color): void
149
    {
150 23
        $this->interval = $this->settings->getInterval();
151 23
        $this->frameErasingShift = $this->settings->getErasingShift();
152 23
        $this->inlinePaddingStr = $this->settings->getInlinePaddingStr();
153 23
        $this->currentMessage = $this->settings->getMessage();
154 23
        $this->messageErasingLen = $this->settings->getMessageErasingLen();
155 23
        $this->currentMessagePrefix = $this->settings->getMessagePrefix();
156 23
        $this->currentMessageSuffix = $this->settings->getMessageSuffix();
157 23
        $this->spacer = $this->settings->getSpacer();
158 23
        $this->symbols = new Circular($this->settings->getSymbols());
159
160
        try {
161 23
            $this->style = new Style($this->settings->getStyles(), $color);
162 1
        } catch (\Throwable $e) {
163 1
            throw new \InvalidArgumentException(
164 1
                '[' . static::class . '] ' . $e->getMessage(),
165 1
                (int)$e->getCode(),
166 1
                $e
167
            );
168
        }
169 22
        $this->inlinePaddingStrLen = strlen($this->inlinePaddingStr); // TODO fix code duplicate?
170 22
        $this->currentMessagePrefixLen = strlen($this->currentMessagePrefix);
171 22
        $this->currentMessageSuffixLen = strlen($this->currentMessageSuffix);
172 22
        $this->messageStr = $this->prepareMessageStr();
173 22
        $this->updateProperties();
174 22
    }
175
176 22
    protected function prepareMessageStr(): string
177
    {
178
        return
179 22
            $this->spacer .
180 22
            $this->currentMessagePrefix .
181 22
            ucfirst($this->currentMessage) .
182 22
            $this->currentMessageSuffix;
183
    }
184
185 22
    protected function updateProperties(): void
186
    {
187 22
        $this->percentSpacer = $this->getPercentSpacer(); // TODO move to other location - optimize performance
188
        $strLen =
189 22
            $this->currentMessagePrefixLen +
190 22
            $this->messageErasingLen +
191 22
            $this->currentMessageSuffixLen +
192 22
            $this->percentStrLen +
193 22
            $this->inlinePaddingStrLen +
194 22
            $this->frameErasingShift;
195 22
        $this->moveBackSequenceStr = ESC . "[{$strLen}D";
196 22
        $this->eraseBySpacesStr = str_repeat(SettingsInterface::ONE_SPACE_SYMBOL, $strLen);
197 22
    }
198
199
    /**
200
     * @return string
201
     */
202 22
    protected function getPercentSpacer(): string
203
    {
204 22
        if (strpos($this->messageStr, SettingsInterface::DEFAULT_SUFFIX)) {
205 19
            return SettingsInterface::ONE_SPACE_SYMBOL;
206
        }
207 3
        return SettingsInterface::EMPTY;
208
    }
209
210
    /** {@inheritDoc} */
211 3
    public function getOutput(): ?SpinnerOutputInterface
212
    {
213 3
        return $this->output;
214
    }
215
216 3
    public function interval(): float
217
    {
218 3
        return $this->interval;
219
    }
220
221 4
    public function inline(bool $inline): SpinnerInterface
222
    {
223 4
        $this->inlinePaddingStr = $inline ? SettingsInterface::ONE_SPACE_SYMBOL : SettingsInterface::EMPTY;
224 4
        $this->inlinePaddingStrLen = strlen($this->inlinePaddingStr);
225 4
        $this->updateProperties();
226 4
        return $this;
227
    }
228
229
    /** {@inheritDoc} */
230 17
    public function begin(?float $percent = null): string
231
    {
232 17
        if ($this->output) {
233 3
            $this->output->write(Cursor::hide());
234 3
            $this->spin($percent);
235 3
            return '';
236
        }
237 14
        return Cursor::hide() . $this->spin($percent);
238
    }
239
240
    /** {@inheritDoc} */
241 17
    public function spin(?float $percent = null, ?string $message = null): string
242
    {
243 17
        $this->update($percent, $message);
244 17
        if ($this->output) {
245 3
            $this->output->write($this->preparedStr());
246 3
            return '';
247
        }
248
        return
249 14
            $this->preparedStr();
250
    }
251
252
    /**
253
     * @param null|float $percent
254
     * @param null|string $message
255
     */
256 17
    protected function update(?float $percent, ?string $message): void
257
    {
258 17
        if ((null !== $percent) && 0 === ($percentVal = (int)($percent * 1000)) % 10) {
259 4
            $this->percentStr = $this->percentSpacer . ($percentVal / 10) . '%';
260 4
            $this->percentStrLen = strlen($this->percentStr);
261
        }
262 17
        if ((null !== $message) && $this->currentMessage !== $message) {
263 2
            $this->currentMessage = $message;
264 2
            $this->messageErasingLen = strlen($message);
265 2
            $this->messageStr = $this->prepareMessageStr();
266
        }
267 17
        if (null !== $percent || null !== $message) {
268 6
            $this->updateProperties();
269
        }
270 17
    }
271
272
    /**
273
     * @return string
274
     */
275 17
    protected function preparedStr(): string
276
    {
277
        return
278 17
            $this->inlinePaddingStr .
279 17
            $this->style->spinner((string)$this->symbols->value()) .
280 17
            $this->style->message(
281 17
                $this->message()
282
            ) .
283 17
            $this->style->percent(
284 17
                $this->percent()
285
            ) .
286 17
            $this->moveBackSequenceStr;
287
    }
288
289
    /**
290
     * @return string
291
     */
292 17
    protected function message(): string
293
    {
294 17
        return $this->messageStr;
295
    }
296
297
    /**
298
     * @return string
299
     */
300 17
    protected function percent(): string
301
    {
302 17
        return $this->percentStr;
303
    }
304
305
    /** {@inheritDoc} */
306 16
    public function end(): string
307
    {
308 16
        if ($this->output) {
309 3
            $this->erase();
310 3
            $this->output->write(Cursor::show());
311 3
            return '';
312
        }
313 13
        return $this->erase() . Cursor::show();
314
    }
315
316
    /** {@inheritDoc} */
317 16
    public function erase(): string
318
    {
319 16
        $str = $this->eraseBySpacesStr . $this->moveBackSequenceStr;
320 16
        if ($this->output) {
321 3
            $this->output->write($str);
322 3
            return '';
323
        }
324 13
        return $str;
325
    }
326
327
    /** {@inheritDoc} */
328 1
    public function getSettings(): SettingsInterface
329
    {
330 1
        throw new \RuntimeException(static::class . ': Call to unimplemented functionality ' . __METHOD__);
331
        return $this->settings;
0 ignored issues
show
Unused Code introduced by
return $this->settings is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
332
    }
333
}
334