Passed
Push — master ( 82c48c...eca042 )
by Alec
02:50
created

Spinner::refineOutput()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 4
dl 0
loc 7
ccs 5
cts 5
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 20
    public function __construct($settings = null, $output = null, $color = null)
53
    {
54 20
        $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 20
    protected function refineOutput($output): ?SpinnerOutputInterface
78
    {
79 20
        $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 20
    protected function assertOutput($output): void
90
    {
91 20
        if (null !== $output && false !== $output && !$output instanceof SpinnerOutputInterface) {
92 1
            throw new \InvalidArgumentException(
93 1
                'Incorrect $output param [null|false|SpinnerOutputInterface] expected "' . typeOf($output) . '" given.'
94
            );
95
        }
96 19
    }
97
98
    /**
99
     * @param mixed $settings
100
     * @return SettingsInterface
101
     */
102 19
    protected function refineSettings($settings): SettingsInterface
103
    {
104 19
        $this->assertSettings($settings);
105 18
        if (\is_string($settings)) {
106
            return
107 13
                $this->defaultSettings()->setMessage($settings);
108
        }
109
        return
110 5
            $settings ?? $this->defaultSettings();
111
    }
112
113
    /**
114
     * @param mixed $settings
115
     */
116 19
    protected function assertSettings($settings): void
117
    {
118 19
        if (null !== $settings && !\is_string($settings) && !$settings instanceof SettingsInterface) {
119 1
            throw new \InvalidArgumentException(
120 1
                'Instance of SettingsInterface or string expected ' . typeOf($settings) . ' given.'
121
            );
122
        }
123 18
    }
124
125
    /**
126
     * @return SettingsInterface
127
     */
128 15
    protected function defaultSettings(): SettingsInterface
129
    {
130
        return
131 15
            (new Settings())
132 15
                ->setInterval(static::INTERVAL)
133 15
                ->setErasingShift(static::ERASING_SHIFT)
134 15
                ->setSymbols(static::SYMBOLS)
135 15
                ->setStyles(static::STYLES);
136
    }
137
138
    /**
139
     * @param SettingsInterface $settings
140
     * @return string
141
     */
142 18
    protected function getMessageStr(SettingsInterface $settings): string
143
    {
144 18
        return $settings->getPrefix() . ucfirst($settings->getMessage()) . $settings->getSuffix();
145
    }
146
147 18
    protected function setFields(): void
148
    {
149 18
        $this->percentPrefix = $this->getPercentPrefix();
150
        $strLen =
151 18
            strlen($this->message()) + strlen($this->percent()) + strlen($this->inlinePaddingStr) + $this->erasingShift;
152 18
        $this->moveBackSequenceStr = ESC . "[{$strLen}D";
153 18
        $this->eraseBySpacesStr = str_repeat(SettingsInterface::ONE_SPACE_SYMBOL, $strLen);
154 18
    }
155
156
    /**
157
     * @return string
158
     */
159 18
    protected function getPercentPrefix(): string
160
    {
161 18
        if (strpos($this->messageStr, SettingsInterface::DEFAULT_SUFFIX)) {
162 15
            return SettingsInterface::ONE_SPACE_SYMBOL;
163
        }
164 3
        return SettingsInterface::EMPTY;
165
    }
166
167
    /**
168
     * @return string
169
     */
170 18
    protected function message(): string
171
    {
172 18
        return $this->messageStr;
173
    }
174
175
    /**
176
     * @return string
177
     */
178 18
    protected function percent(): string
179
    {
180 18
        return $this->percentStr;
181
    }
182
183 2
    public function interval(): float
184
    {
185 2
        return $this->interval;
186
    }
187
188 4
    public function inline(bool $inline): SpinnerInterface
189
    {
190 4
        $this->inlinePaddingStr = $inline ? SettingsInterface::ONE_SPACE_SYMBOL : SettingsInterface::EMPTY;
191 4
        $this->setFields();
192 4
        return $this;
193
    }
194
195
    /** {@inheritDoc} */
196 14
    public function begin(?float $percent = null): string
197
    {
198 14
        if ($this->output) {
199 3
            $this->output->write(Cursor::hide());
200 3
            $this->spin($percent);
201 3
            return '';
202
        }
203 11
        return Cursor::hide() . $this->spin($percent);
204
    }
205
206
    /** {@inheritDoc} */
207 14
    public function spin(?float $percent = null): string
208
    {
209 14
        if (null !== $percent) {
210 4
            $this->updatePercent($percent);
211
        }
212 14
        $str = $this->inlinePaddingStr .
213 14
            $this->style->spinner((string)$this->symbols->value()) .
214 14
            $this->style->message(
215 14
                $this->message()
216
            ) .
217 14
            $this->style->percent(
218 14
                $this->percent()
219
            ) .
220 14
            $this->moveBackSequenceStr;
221 14
        if ($this->output) {
222 3
            $this->output->write($str);
223 3
            return '';
224
        }
225
        return
226 11
            $str;
227
    }
228
229
    /**
230
     * @param float $percent
231
     */
232 4
    protected function updatePercent(float $percent): void
233
    {
234 4
        if (0 === (int)($percent * 1000) % 10) {
235 4
            $this->percentStr = Pretty::percent($percent, 0, $this->percentPrefix);
236 4
            $this->setFields();
237
        }
238 4
    }
239
240
    /** {@inheritDoc} */
241 14
    public function end(): string
242
    {
243 14
        if ($this->output) {
244 3
            $this->erase();
245 3
            $this->output->write(Cursor::show());
246 3
            return '';
247
        }
248 11
        return $this->erase() . Cursor::show();
249
    }
250
251
    /** {@inheritDoc} */
252 14
    public function erase(): string
253
    {
254 14
        $str = $this->eraseBySpacesStr . $this->moveBackSequenceStr;
255 14
        if ($this->output) {
256 3
            $this->output->write($str);
257 3
            return '';
258
        }
259 11
        return $str;
260
    }
261
}
262