Passed
Push — master ( 619655...cd9add )
by Caen
03:39 queued 14s
created

ConsoleOutput::renderLine()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 2
c 1
b 0
f 0
nc 2
nop 1
dl 0
loc 4
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Hyde\RealtimeCompiler;
6
7
use Closure;
8
use Hyde\Hyde;
9
use Illuminate\Support\Str;
10
use Illuminate\Support\Carbon;
11
use Symfony\Component\Console\Output\ConsoleOutput as SymfonyOutput;
12
13
use function Termwind\render;
14
15
class ConsoleOutput
16
{
17
    protected bool $verbose;
18
    protected SymfonyOutput $output;
19
20
    public function __construct(bool $verbose = false, ?SymfonyOutput $output = null)
21
    {
22
        $this->verbose = $verbose;
23
        $this->output = $output ?? new SymfonyOutput();
24
    }
25
26
    public function printStartMessage(string $host, int $port): void
27
    {
28
        $title = 'HydePHP Realtime Compiler';
29
        $version = ' v'.Hyde::version();
30
31
        $url = sprintf('%s://%s:%d', $port === 443 ? 'https' : 'http', $host, $port);
32
33
        $width = max(strlen("$title $version"), strlen("Listening on $url") + 1) + 1;
34
        $spacing = str_repeat('&nbsp;', $width);
35
        $lines = str_repeat('─', $width);
36
37
        $line1 = '&nbsp;'.sprintf('<span class="text-blue-500">%s</span>&nbsp;<span class="text-gray">%s</span>', $title, $version).str_repeat('&nbsp;', $width - strlen("$title $version"));
38
        $line2 = '&nbsp;'.sprintf('<span class="text-white">Listening on </span>&nbsp;<a href="%s" class="text-yellow-500">%s</a>', $url, $url).str_repeat('&nbsp;', $width - strlen("Listening on $url") - 1);
39
        render(<<<HTML
40
<div class="text-green-500">
41
<br>
42
&nbsp;╭{$lines}╮<br>
43
&nbsp;│{$spacing}│<br>
44
&nbsp;│{$line1}│<br>
45
&nbsp;│{$spacing}│<br>
46
&nbsp;│{$line2}│<br>
47
&nbsp;│{$spacing}│<br>
48
&nbsp;╰{$lines}╯
49
<br>
50
</div>
51
HTML);
52
    }
53
54
    public function getFormatter(): Closure
55
    {
56
        return function (string $type, string $line): void {
57
            $this->handleOutput($line);
58
        };
59
    }
60
61
    /** @experimental */
62
    public function printMessage(string $message, string $context): void
63
    {
64
        $this->output->writeln(sprintf('%s [%s]', $message, $context));
65
    }
66
67
    protected function handleOutput(string $buffer): void
68
    {
69
        str($buffer)->trim()->explode("\n")->each(function (string $line): void {
70
            $this->renderLine($this->formatLineForOutput($line));
71
        });
72
    }
73
74
    protected function renderLine(?string $line): void
75
    {
76
        if ($line !== null) {
77
            render($line);
78
        }
79
    }
80
81
    protected function formatLineForOutput(string $line): ?string
82
    {
83
        if (str_contains($line, 'Development Server (http:')) {
84
            return $this->formatServerStartedLine($line);
85
        }
86
        if (str_contains($line, ']: ')) {
87
            return $this->formatRequestLine($line);
88
        }
89
        if (str_ends_with(trim($line), 'Accepted') || str_ends_with(trim($line), 'Closing')) {
90
            return $this->verbose ? $this->formatRequestStatusLine($line) : null;
91
        }
92
        if (str_contains($line, '[dashboard@')) {
93
            return $this->formatDashboardContextLine($line);
94
        }
95
96
        return $this->formatLine($line, Carbon::now());
97
    }
98
99
    protected function formatServerStartedLine(string $line): string
100
    {
101
        return $this->formatLine(sprintf('PHP %s Development Server started. <span class="text-yellow-500">Press Ctrl+C to stop.</span>', PHP_VERSION), $this->parseDate($line), 'green-500');
102
    }
103
104
    protected function formatRequestLine(string $line): string
105
    {
106
        $dateString = Str::betweenFirst($line, '[', ']');
107
        $message = substr($line, strlen($dateString) + 3);
108
109
        $statusCode = Str::between($message, ' [', ']:');
110
        if ($statusCode >= 400) {
111
            $message = str_replace($statusCode, sprintf('<span class="text-red-500">%s</span>', $statusCode), $message);
112
            $iconColor = 'yellow-500';
113
        }
114
115
        return $this->formatLine($message, $this->parseDate($line), $iconColor ?? 'blue-500');
116
    }
117
118
    protected function formatRequestStatusLine(string $line): string
119
    {
120
        $address = trim(Str::between($line, ']', ' '));
121
        $status = str_contains($line, 'Accepted') ? 'Accepted' : 'Closing';
122
123
        return $this->formatLine(sprintf('%s %s', $address, $status), $this->parseDate($line));
124
    }
125
126
    protected function formatDashboardContextLine(string $line): string
127
    {
128
        $message = trim(Str::before($line, '[dashboard@'));
129
        $context = trim(trim(Str::after($line, $message)), '[]');
130
        $success = str_contains($message, 'Created') || str_contains($message, 'Updated');
131
132
        return $this->formatLine($message, Carbon::now(), $success ? 'green-500' : 'blue-500', $context);
133
    }
134
135
    protected function formatLine(string $message, Carbon $date, string $iconColor = 'blue-500', string $context = ''): string
136
    {
137
        if ($context) {
138
            $context = "$context ";
139
        }
140
141
        return sprintf(<<<'HTML'
142
            <div class="flex w-full justify-between">
143
                <span>
144
                    <span class="text-%s">i</span>
145
                    %s
146
                </span>
147
                <span class="text-gray">%s%s</span>
148
            </div>
149
            HTML,
150
            $iconColor, $message, $context, $date->format('Y-m-d H:i:s')
151
        );
152
    }
153
154
    protected function parseDate(string $line): Carbon
155
    {
156
        return Carbon::parse(Str::betweenFirst($line, '[', ']'));
157
    }
158
}
159