Passed
Push — master ( 07b99e...8b173b )
by Alec
02:40
created

Spinner::updateMessage()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

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