Passed
Pull Request — master (#4)
by
unknown
01:33
created

Spinner::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 4
Bugs 0 Features 2
Metric Value
eloc 4
c 4
b 0
f 2
dl 0
loc 6
ccs 4
cts 4
cp 1
rs 10
cc 1
nc 1
nop 1
crap 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace AlecRabbit\Snake;
6
7
use AlecRabbit\Snake\Contracts\Color;
8
use AlecRabbit\Snake\Contracts\SpinnerInterface;
9
use AlecRabbit\Snake\Core\Driver;
10
11
class Spinner implements SpinnerInterface
12
{
13
    private const CHARS = ['⠏', '⠛', '⠹', '⢸', '⣰', '⣤', '⣆', '⡇'];
14
    private const COLORS = [
15
        196,
16
        196,
17
        202,
18
        202,
19
        208,
20
        208,
21
        214,
22
        214,
23
        220,
24
        220,
25
        226,
26
        226,
27
        190,
28
        190,
29
        154,
30
        154,
31
        118,
32
        118,
33
        82,
34
        82,
35
        46,
36
        46,
37
        47,
38
        47,
39
        48,
40
        48,
41
        49,
42
        49,
43
        50,
44
        50,
45
        51,
46
        51,
47
        45,
48
        45,
49
        39,
50
        39,
51
        33,
52
        33,
53
        27,
54
        27,
55
        56,
56
        56,
57
        57,
58
        57,
59
        93,
60
        93,
61
        129,
62
        129,
63
        165,
64
        165,
65
        201,
66
        201,
67
        200,
68
        200,
69
        199,
70
        199,
71
        198,
72
        198,
73
        197,
74
        197,
75
    ];
76
77
    /** @var Driver */
78
    private $driver;
79
    /** @var int */
80
    private $currentCharIdx = 0;
81
    /** @var int */
82
    private $currentColorIdx = 0;
83
    /** @var int */
84
    private $framesCount;
85
    /** @var int */
86
    private $colorCount;
87
    /** @var string|null */
88 3
    protected $message;
89
    /** @var int */
90 3
    protected $terminalCols;
91 1
    /** @var float */
92 1
    protected $lastFrameTimestamp = 0;
93 1
94
    public function __construct(int $colorLevel = Color::COLOR_256)
95 1
    {
96
        $this->driver = new Driver($colorLevel);
97 1
        $this->framesCount = count(self::CHARS);
98 1
        $this->colorCount = count(self::COLORS);
99 1
        $this->terminalCols = (int) (exec('tput cols') ?? 80);
100 1
    }
101 1
102
    public function spin(): void
103 1
    {
104
        $message = is_string($this->message) && !empty($this->message)
105 1
            ? ' ' . $this->message
106 1
            : '';
107
108 1
        $output =
109
            $this->driver->eraseSequence()
110 1
            . $this->driver->frameSequence(
111 1
                self::COLORS[$this->currentColorIdx],
112
                self::CHARS[$this->currentCharIdx]
113 1
            )
114
            . $message;
115
116 1
        $spaces = $this->terminalCols - mb_strlen($output);
117
        $output = $output . str_repeat(' ', max(0, $spaces));
118
119
        $this->driver->write(
120
            $output,
121
            "\r"
122 1
        );
123
        $this->update();
124 1
    }
125
126
    private function update(): void
127 1
    {
128
        /** @var float $now */
129 1
        $now = microtime(true);
130 1
        if ($now >= $this->lastFrameTimestamp + $this->interval()) {
131
            $this->lastFrameTimestamp = $now;
132 1
133
            if (++$this->currentCharIdx === $this->framesCount) {
134 1
                $this->currentCharIdx = 0;
135 1
            }
136 1
            if (++$this->currentColorIdx === $this->colorCount) {
137
                $this->currentColorIdx = 0;
138 1
            }
139
        }
140 1
    }
141 1
142
    /** @inheritDoc */
143 1
    public function interval(): float
144
    {
145
        return 0.1;
146
    }
147
148
    public function begin(): void
149
    {
150
        $this->driver->hideCursor();
151
    }
152
153
    public function end(): void
154
    {
155
        $this->erase();
156
        $this->driver->showCursor();
157
    }
158
159
    public function erase(): void
160
    {
161
        $this->driver->write(
162
            $this->driver->eraseSequence()
163
        );
164
    }
165
166
    public function useStdOut(): void
167
    {
168
        $this->driver->useStdOut();
169
    }
170
171
    /** @inheritDoc */
172
    public function setMessage(?string $message): void
173
    {
174
        if ($message === null) {
175
            $this->message = null;
176
            return;
177
        }
178
179
        $message = mb_substr($message, 0, $this->terminalCols - 10);
180
        $breakPosition = mb_strpos($message, "\n");
181
182
        if ($breakPosition !== false) {
183
            $message = mb_substr($message, 0, $breakPosition);
184
        }
185
186
        $this->message = trim($message);
187
    }
188
}
189