From 1f69d72ccff2d54af9cca96fd8f4380a5e13aa71 Mon Sep 17 00:00:00 2001 From: Wendell Adriel Date: Mon, 22 May 2023 16:18:36 +0100 Subject: [PATCH 01/12] Initial output layer implementation --- src/Configuration/Config.php | 46 ++++++- src/Contracts/Output/EngineContract.php | 48 ++++++++ src/Minicli.php | 30 +++++ src/Output/Engine.php | 25 ---- src/Output/Engine/DefaultEngine.php | 154 ++++++++++++++++++++++++ src/Output/Table.php | 9 ++ 6 files changed, 286 insertions(+), 26 deletions(-) create mode 100644 src/Contracts/Output/EngineContract.php delete mode 100644 src/Output/Engine.php create mode 100644 src/Output/Engine/DefaultEngine.php create mode 100644 src/Output/Table.php diff --git a/src/Configuration/Config.php b/src/Configuration/Config.php index 1ef2494..0e9e103 100644 --- a/src/Configuration/Config.php +++ b/src/Configuration/Config.php @@ -4,6 +4,8 @@ namespace Minicli\Framework\Configuration; +use Minicli\Framework\Contracts\Output\EngineContract; +use Minicli\Framework\Contracts\Output\PrinterContract; use Minicli\Framework\Contracts\Theme\ThemeContract; final class Config @@ -11,11 +13,15 @@ final class Config /** * @param string $path * @param class-string|null $theme + * @param class-string|null $printer + * @param class-string|null $engine * @param bool $debug */ public function __construct( private readonly string $path, private readonly ?string $theme = null, + private readonly ?string $printer = null, + private readonly ?string $engine = null, private readonly bool $debug = false, ) { } @@ -28,12 +34,14 @@ public function toArray(): array return [ 'path' => $this->path, 'theme' => $this->theme, + 'printer' => $this->printer, + 'engine' => $this->engine, 'debug' => $this->debug, ]; } /** - * @param array{path:string,theme:class-string|null,debug:bool|null} $data + * @param array{path:string,theme:class-string|null,printer:class-string|null,engine:class-string|null,debug:bool|null} $data * @return Config */ public static function make(array $data): Config @@ -41,7 +49,43 @@ public static function make(array $data): Config return new Config( path: $data['path'], theme: $data['theme'], + printer: $data['printer'], + engine: $data['engine'], debug: $data['debug'] ?? false, ); } + + public function path(): string + { + return $this->path; + } + + /** + * @return class-string|null + */ + public function theme(): ?string + { + return $this->theme; + } + + /** + * @return class-string|null + */ + public function printer(): ?string + { + return $this->printer; + } + + /** + * @return class-string|null + */ + public function engine(): ?string + { + return $this->engine; + } + + public function debug(): bool + { + return $this->debug; + } } diff --git a/src/Contracts/Output/EngineContract.php b/src/Contracts/Output/EngineContract.php new file mode 100644 index 0000000..f5d1418 --- /dev/null +++ b/src/Contracts/Output/EngineContract.php @@ -0,0 +1,48 @@ +loadEngine(); $this->loadCommands(); } /** * @param string $path * @param class-string|null $theme + * @param class-string|null $printer + * @param class-string|null $engine * @param bool $debug * @return Minicli */ public static function boot( string $path, ?string $theme, + ?string $printer, + ?string $engine, bool $debug = false, ): Minicli { $config = new Config( path: $path, theme: $theme, + printer: $printer, + engine: $engine, debug: $debug, ); @@ -63,6 +75,24 @@ public function run(array $argv = []): void $command->teardown(); } + private function loadEngine(): void + { + $this->singleton( + abstract: ThemeContract::class, + concrete: $this->config->theme() ?? DefaultTheme::class, + ); + + $this->singleton( + abstract: PrinterContract::class, + concrete: $this->config->printer() ?? DefaultPrinter::class, + ); + + $this->singleton( + abstract: EngineContract::class, + concrete: $this->config->engine() ?? DefaultEngine::class, + ); + } + /** * Loads commands into the DI container. */ diff --git a/src/Output/Engine.php b/src/Output/Engine.php deleted file mode 100644 index d08aea0..0000000 --- a/src/Output/Engine.php +++ /dev/null @@ -1,25 +0,0 @@ -printer->print( - message: $message, - ); - } -} diff --git a/src/Output/Engine/DefaultEngine.php b/src/Output/Engine/DefaultEngine.php new file mode 100644 index 0000000..428bd39 --- /dev/null +++ b/src/Output/Engine/DefaultEngine.php @@ -0,0 +1,154 @@ +printer->print($message); + } + + public function newLine(): void + { + print $this->printer->print("\n"); + } + + public function table(Table $table): void + { + // TODO: Implement table() method. + } + + public function ask(string $question): string + { + // TODO: Implement ask() method. + return ''; + } + + public function default(string $message): void + { + print $this->printer->print( + message: $this->format($message, $this->theme->default()), + ); + } + + public function alt(string $message): void + { + print $this->printer->print( + message: $this->format($message, $this->theme->alt()), + ); + } + + public function error(string $message): void + { + print $this->printer->print( + message: $this->format($message, $this->theme->error()), + ); + } + + public function errorAlt(string $message): void + { + print $this->printer->print( + message: $this->format($message, $this->theme->errorAlt()), + ); + } + + public function warning(string $message): void + { + print $this->printer->print( + message: $this->format($message, $this->theme->warning()), + ); + } + + public function warningAlt(string $message): void + { + print $this->printer->print( + message: $this->format($message, $this->theme->warningAlt()), + ); + } + + public function success(string $message): void + { + print $this->printer->print( + message: $this->format($message, $this->theme->success()), + ); + } + + public function successAlt(string $message): void + { + print $this->printer->print( + message: $this->format($message, $this->theme->successAlt()), + ); + } + + public function info(string $message): void + { + print $this->printer->print( + message: $this->format($message, $this->theme->info()), + ); + } + + public function infoAlt(string $message): void + { + print $this->printer->print( + message: $this->format($message, $this->theme->infoAlt()), + ); + } + + public function bold(string $message): void + { + print $this->printer->print( + message: $this->format($message, $this->theme->bold()), + ); + } + + public function dim(string $message): void + { + print $this->printer->print( + message: $this->format($message, $this->theme->dim()), + ); + } + + public function italic(string $message): void + { + print $this->printer->print( + message: $this->format($message, $this->theme->italic()), + ); + } + + public function underline(string $message): void + { + print $this->printer->print( + message: $this->format($message, $this->theme->underline()), + ); + } + + public function invert(string $message): void + { + print $this->printer->print( + message: $this->format($message, $this->theme->invert()), + ); + } + + private function format(string $message, ThemeStyle $style): string + { + $foreground = $style->foreground->value; + $background = $style->background?->value ?? ''; + + return sprintf("\e[%s%sm%s\e[0m", $foreground, $background, $message); + } +} diff --git a/src/Output/Table.php b/src/Output/Table.php new file mode 100644 index 0000000..d481f5f --- /dev/null +++ b/src/Output/Table.php @@ -0,0 +1,9 @@ + Date: Mon, 22 May 2023 18:16:28 +0100 Subject: [PATCH 02/12] Table implementation for the default engine --- src/Output/Engine/DefaultEngine.php | 4 +- src/Output/Table.php | 82 +++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+), 1 deletion(-) diff --git a/src/Output/Engine/DefaultEngine.php b/src/Output/Engine/DefaultEngine.php index 428bd39..79381ee 100644 --- a/src/Output/Engine/DefaultEngine.php +++ b/src/Output/Engine/DefaultEngine.php @@ -30,7 +30,9 @@ public function newLine(): void public function table(Table $table): void { - // TODO: Implement table() method. + $this->newLine(); + $this->print($table->render()); + $this->newLine(); } public function ask(string $question): string diff --git a/src/Output/Table.php b/src/Output/Table.php index d481f5f..e6b0254 100644 --- a/src/Output/Table.php +++ b/src/Output/Table.php @@ -6,4 +6,86 @@ final class Table { + private const MIN_COLUMN_SIZE = 5; + + /** + * @param array $headers + * @param array> $rows + */ + public function __construct( + private array $headers, + private array $rows = [], + ) { + } + + /** + * @param array $row + */ + public function addRow(array $row): Table + { + $this->rows[] = $row; + + return $this; + } + + public function totalRows(): int + { + return count($this->rows); + } + + public function render(): string + { + $table = [$this->headers, ...$this->rows]; + $columnSizes = $this->calculateColumnSizes($table); + + $formattedTable = ''; + foreach ($table as $row) { + $formattedTable .= $this->getRowAsString($row, $columnSizes); + } + + return $formattedTable; + } + + /** + * @param array> $table + * @return array + */ + private function calculateColumnSizes(array $table, int $minColSize = self::MIN_COLUMN_SIZE): array + { + $columnSizes = []; + + foreach ($table as $row) { + $columnCount = 0; + + foreach ($row as $cell) { + $columnSizes[$columnCount] = $columnSizes[$columnCount] ?? $minColSize; + if (mb_strlen($cell) >= $columnSizes[$columnCount]) { + $columnSizes[$columnCount] = mb_strlen($cell) + 2; + } + $columnCount++; + } + } + + return $columnSizes; + } + + /** + * @param array $row + * @param array $columnSizes + */ + private function getRowAsString(array $row, array $columnSizes): string + { + $formattedRow = ''; + + foreach ($row as $column => $cell) { + $formattedRow .= $this->getPaddedString($cell, $columnSizes[$column]); + } + + return $formattedRow; + } + + private function getPaddedString(string $cell, int $colSize = self::MIN_COLUMN_SIZE): string + { + return str_pad($cell, $colSize); + } } From 0da529cf1c71bccdd8932c35c9d6e339d1e36fcb Mon Sep 17 00:00:00 2001 From: Wendell Adriel Date: Mon, 22 May 2023 18:20:17 +0100 Subject: [PATCH 03/12] Rename newLine to lineBreak --- src/Contracts/Output/EngineContract.php | 2 +- src/Output/Engine/DefaultEngine.php | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Contracts/Output/EngineContract.php b/src/Contracts/Output/EngineContract.php index f5d1418..eab629d 100644 --- a/src/Contracts/Output/EngineContract.php +++ b/src/Contracts/Output/EngineContract.php @@ -10,7 +10,7 @@ interface EngineContract { public function print(string $message): void; - public function newLine(): void; + public function lineBreak(): void; public function table(Table $table): void; diff --git a/src/Output/Engine/DefaultEngine.php b/src/Output/Engine/DefaultEngine.php index 79381ee..b7dd25b 100644 --- a/src/Output/Engine/DefaultEngine.php +++ b/src/Output/Engine/DefaultEngine.php @@ -23,16 +23,16 @@ public function print(string $message): void print $this->printer->print($message); } - public function newLine(): void + public function lineBreak(): void { print $this->printer->print("\n"); } public function table(Table $table): void { - $this->newLine(); + $this->lineBreak(); $this->print($table->render()); - $this->newLine(); + $this->lineBreak(); } public function ask(string $question): string From 715692d1d17e4e39a911871c2e286396d154869b Mon Sep 17 00:00:00 2001 From: Wendell Adriel Date: Mon, 22 May 2023 18:39:24 +0100 Subject: [PATCH 04/12] Add ask implementation --- composer.json | 1 + src/Contracts/Output/EngineContract.php | 2 +- src/Input/AskInput.php | 48 +++++++++++++++++++++++++ src/Output/Engine/DefaultEngine.php | 13 +++++-- 4 files changed, 60 insertions(+), 4 deletions(-) create mode 100644 src/Input/AskInput.php diff --git a/composer.json b/composer.json index da8a56b..049c102 100644 --- a/composer.json +++ b/composer.json @@ -11,6 +11,7 @@ ], "require": { "php": "^8.1", + "ext-readline": "*", "filp/whoops": "^2.15", "minicli/mini-plugin": "dev-main" }, diff --git a/src/Contracts/Output/EngineContract.php b/src/Contracts/Output/EngineContract.php index eab629d..df88e25 100644 --- a/src/Contracts/Output/EngineContract.php +++ b/src/Contracts/Output/EngineContract.php @@ -14,7 +14,7 @@ public function lineBreak(): void; public function table(Table $table): void; - public function ask(string $question): string; + public function ask(string $question, string $method = 'default'): string; public function default(string $message): void; diff --git a/src/Input/AskInput.php b/src/Input/AskInput.php new file mode 100644 index 0000000..44cd2f3 --- /dev/null +++ b/src/Input/AskInput.php @@ -0,0 +1,48 @@ + $inputHistory + */ + public function __construct( + private string $prompt = '> ', + private array $inputHistory = [], + ) { + } + + public static function make(string $prompt = '> ', array $inputHistory = []): AskInput + { + return new self($prompt, $inputHistory); + } + + public function read(): string + { + $input = (string) readline($this->prompt); + $this->inputHistory[] = $input; + + return $input; + } + + public function prompt(): string + { + return $this->prompt; + } + + public function history(): array + { + return $this->inputHistory; + } + + public function setPrompt(string $prompt): AskInput + { + $this->prompt = $prompt; + + return $this; + } +} diff --git a/src/Output/Engine/DefaultEngine.php b/src/Output/Engine/DefaultEngine.php index b7dd25b..ce9bede 100644 --- a/src/Output/Engine/DefaultEngine.php +++ b/src/Output/Engine/DefaultEngine.php @@ -4,9 +4,11 @@ namespace Minicli\Framework\Output\Engine; +use http\Exception\InvalidArgumentException; use Minicli\Framework\Contracts\Output\EngineContract; use Minicli\Framework\Contracts\Output\PrinterContract; use Minicli\Framework\Contracts\Theme\ThemeContract; +use Minicli\Framework\Input\AskInput; use Minicli\Framework\Output\Table; use Minicli\Framework\Theme\ThemeStyle; @@ -35,10 +37,15 @@ public function table(Table $table): void $this->lineBreak(); } - public function ask(string $question): string + public function ask(string $question, string $method = 'default'): string { - // TODO: Implement ask() method. - return ''; + if ( ! method_exists($this, $method)) { + throw new InvalidArgumentException("No output for [{$method}]"); + } + + $this->{$method}($question); + + return AskInput::make()->read(); } public function default(string $message): void From d2be9585698781665715e7fba1d112a808265a67 Mon Sep 17 00:00:00 2001 From: Wendell Adriel Date: Tue, 23 May 2023 12:54:38 +0100 Subject: [PATCH 05/12] Update exception message --- src/Exceptions/MissingParametersException.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Exceptions/MissingParametersException.php b/src/Exceptions/MissingParametersException.php index aa193a1..5c93c0c 100644 --- a/src/Exceptions/MissingParametersException.php +++ b/src/Exceptions/MissingParametersException.php @@ -11,7 +11,7 @@ final class MissingParametersException extends Exception public function __construct(array $missing) { parent::__construct(sprintf( - 'Missing required parameters: %s', + 'Missing required parameter(s): %s', implode(', ', $missing) )); } From 196332d5bc2c38ce1e9673e8d636c23c3986d09b Mon Sep 17 00:00:00 2001 From: Wendell Adriel Date: Tue, 23 May 2023 13:02:34 +0100 Subject: [PATCH 06/12] Add FilePrinter class --- src/Output/Printer/FilePrinter.php | 42 ++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 src/Output/Printer/FilePrinter.php diff --git a/src/Output/Printer/FilePrinter.php b/src/Output/Printer/FilePrinter.php new file mode 100644 index 0000000..52f9353 --- /dev/null +++ b/src/Output/Printer/FilePrinter.php @@ -0,0 +1,42 @@ +outputFile, 'a+'); + + if (false === $fp) { + throw new TypeError("Could not open file {$this->outputFile} for writing."); + } + + fwrite($fp, $message); + fclose($fp); + + return $message; + } + + public function outputFile(): string + { + return $this->outputFile; + } + + public function setOutputFile(string $outputFile): FilePrinter + { + $this->outputFile = $outputFile; + + return $this; + } +} From 6a52ad5a28bd34214a9e4c2d167c7152942e2a6d Mon Sep 17 00:00:00 2001 From: Wendell Adriel Date: Tue, 23 May 2023 13:09:18 +0100 Subject: [PATCH 07/12] Add built-in Themes --- src/Theme/Catalog/DaltonTheme.php | 89 ++++++++++++++++++++++++++++++ src/Theme/Catalog/DraculaTheme.php | 89 ++++++++++++++++++++++++++++++ src/Theme/Catalog/UnicornTheme.php | 89 ++++++++++++++++++++++++++++++ 3 files changed, 267 insertions(+) create mode 100644 src/Theme/Catalog/DaltonTheme.php create mode 100644 src/Theme/Catalog/DraculaTheme.php create mode 100644 src/Theme/Catalog/UnicornTheme.php diff --git a/src/Theme/Catalog/DaltonTheme.php b/src/Theme/Catalog/DaltonTheme.php new file mode 100644 index 0000000..a2fb65e --- /dev/null +++ b/src/Theme/Catalog/DaltonTheme.php @@ -0,0 +1,89 @@ + Date: Tue, 23 May 2023 13:39:25 +0100 Subject: [PATCH 08/12] Initial implementation for Termwind integration --- src/Theme/Enums/TermwindColor.php | 272 ++++++++++++++++++++++++++++++ src/Theme/ThemeStyle.php | 5 +- 2 files changed, 275 insertions(+), 2 deletions(-) create mode 100644 src/Theme/Enums/TermwindColor.php diff --git a/src/Theme/Enums/TermwindColor.php b/src/Theme/Enums/TermwindColor.php new file mode 100644 index 0000000..0fd66f8 --- /dev/null +++ b/src/Theme/Enums/TermwindColor.php @@ -0,0 +1,272 @@ + Date: Tue, 23 May 2023 14:07:04 +0100 Subject: [PATCH 09/12] Add TermwindTheme --- src/Theme/Catalog/TermwindTheme.php | 87 +++++++++++++++++++ .../{TermwindColor.php => TermwindStyle.php} | 7 +- src/Theme/ThemeStyle.php | 12 +-- 3 files changed, 100 insertions(+), 6 deletions(-) create mode 100644 src/Theme/Catalog/TermwindTheme.php rename src/Theme/Enums/{TermwindColor.php => TermwindStyle.php} (97%) diff --git a/src/Theme/Catalog/TermwindTheme.php b/src/Theme/Catalog/TermwindTheme.php new file mode 100644 index 0000000..d3a3487 --- /dev/null +++ b/src/Theme/Catalog/TermwindTheme.php @@ -0,0 +1,87 @@ + Date: Tue, 23 May 2023 14:09:08 +0100 Subject: [PATCH 10/12] Add TermwindThemeContract --- src/Contracts/Theme/TermwindThemeContract.php | 9 +++++++++ src/Theme/Catalog/TermwindTheme.php | 4 ++-- 2 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 src/Contracts/Theme/TermwindThemeContract.php diff --git a/src/Contracts/Theme/TermwindThemeContract.php b/src/Contracts/Theme/TermwindThemeContract.php new file mode 100644 index 0000000..bae6da6 --- /dev/null +++ b/src/Contracts/Theme/TermwindThemeContract.php @@ -0,0 +1,9 @@ + Date: Tue, 23 May 2023 14:42:35 +0100 Subject: [PATCH 11/12] Add Termwind and Plates services --- composer.json | 4 ++- src/Configuration/Config.php | 13 ++++++++-- src/Minicli.php | 15 +++++++++++ src/Output/Services/Plates.php | 22 ++++++++++++++++ src/Output/Services/Termwind.php | 43 ++++++++++++++++++++++++++++++++ 5 files changed, 94 insertions(+), 3 deletions(-) create mode 100644 src/Output/Services/Plates.php create mode 100644 src/Output/Services/Termwind.php diff --git a/composer.json b/composer.json index 049c102..6242580 100644 --- a/composer.json +++ b/composer.json @@ -13,7 +13,9 @@ "php": "^8.1", "ext-readline": "*", "filp/whoops": "^2.15", - "minicli/mini-plugin": "dev-main" + "league/plates": "^3.5", + "minicli/mini-plugin": "dev-main", + "nunomaduro/termwind": "^1.15" }, "require-dev": { "laravel/pint": "^1.10", diff --git a/src/Configuration/Config.php b/src/Configuration/Config.php index 0e9e103..c463692 100644 --- a/src/Configuration/Config.php +++ b/src/Configuration/Config.php @@ -12,6 +12,7 @@ final class Config { /** * @param string $path + * @param string $views * @param class-string|null $theme * @param class-string|null $printer * @param class-string|null $engine @@ -19,6 +20,7 @@ final class Config */ public function __construct( private readonly string $path, + private readonly string $views, private readonly ?string $theme = null, private readonly ?string $printer = null, private readonly ?string $engine = null, @@ -27,12 +29,13 @@ public function __construct( } /** - * @return array{path:string,theme:class-string|null,debug:bool} + * @return array{path:string,views:string,theme:class-string|null,debug:bool} */ public function toArray(): array { return [ 'path' => $this->path, + 'views' => $this->views, 'theme' => $this->theme, 'printer' => $this->printer, 'engine' => $this->engine, @@ -41,13 +44,14 @@ public function toArray(): array } /** - * @param array{path:string,theme:class-string|null,printer:class-string|null,engine:class-string|null,debug:bool|null} $data + * @param array{path:string,views:string,theme:class-string|null,printer:class-string|null,engine:class-string|null,debug:bool|null} $data * @return Config */ public static function make(array $data): Config { return new Config( path: $data['path'], + views: $data['views'], theme: $data['theme'], printer: $data['printer'], engine: $data['engine'], @@ -60,6 +64,11 @@ public function path(): string return $this->path; } + public function views(): string + { + return $this->views; + } + /** * @return class-string|null */ diff --git a/src/Minicli.php b/src/Minicli.php index df3d31f..42b9a59 100644 --- a/src/Minicli.php +++ b/src/Minicli.php @@ -15,6 +15,8 @@ use Minicli\Framework\Input\Input; use Minicli\Framework\Output\Engine\DefaultEngine; use Minicli\Framework\Output\Printer\DefaultPrinter; +use Minicli\Framework\Output\Services\Plates; +use Minicli\Framework\Output\Services\Termwind; use Minicli\Framework\Theme\Catalog\DefaultTheme; final class Minicli extends Container @@ -32,6 +34,7 @@ public function __construct( /** * @param string $path + * @param string $views * @param class-string|null $theme * @param class-string|null $printer * @param class-string|null $engine @@ -40,6 +43,7 @@ public function __construct( */ public static function boot( string $path, + string $views, ?string $theme, ?string $printer, ?string $engine, @@ -47,6 +51,7 @@ public static function boot( ): Minicli { $config = new Config( path: $path, + views: $views, theme: $theme, printer: $printer, engine: $engine, @@ -91,6 +96,16 @@ private function loadEngine(): void abstract: EngineContract::class, concrete: $this->config->engine() ?? DefaultEngine::class, ); + + $this->singleton( + abstract: Termwind::class, + concrete: Termwind::class, + ); + + $this->singleton( + abstract: Plates::class, + concrete: Plates::class, + ); } /** diff --git a/src/Output/Services/Plates.php b/src/Output/Services/Plates.php new file mode 100644 index 0000000..75bf3da --- /dev/null +++ b/src/Output/Services/Plates.php @@ -0,0 +1,22 @@ +engine = new Engine($path); + } + + public function view(string $template, array $data = []): string + { + return $this->engine->render($template, $data); + } +} diff --git a/src/Output/Services/Termwind.php b/src/Output/Services/Termwind.php new file mode 100644 index 0000000..58bdf05 --- /dev/null +++ b/src/Output/Services/Termwind.php @@ -0,0 +1,43 @@ +|null $autocomplete + * @return mixed + */ + public function ask(string $question, iterable $autocomplete = null): mixed + { + return ask($question, $autocomplete); + } +} From 3f37073e7f4e381e4ccee74c024e75846806ad50 Mon Sep 17 00:00:00 2001 From: Wendell Adriel Date: Tue, 23 May 2023 16:12:15 +0100 Subject: [PATCH 12/12] Add TermwindEngine class --- src/Minicli.php | 22 +-- src/Output/Engine/TermwindEngine.php | 198 +++++++++++++++++++++++++++ src/Output/Table.php | 16 +++ src/Theme/Enums/TermwindStyle.php | 13 ++ 4 files changed, 239 insertions(+), 10 deletions(-) create mode 100644 src/Output/Engine/TermwindEngine.php diff --git a/src/Minicli.php b/src/Minicli.php index 42b9a59..cf366de 100644 --- a/src/Minicli.php +++ b/src/Minicli.php @@ -83,28 +83,30 @@ public function run(array $argv = []): void private function loadEngine(): void { $this->singleton( - abstract: ThemeContract::class, - concrete: $this->config->theme() ?? DefaultTheme::class, + abstract: Termwind::class, + concrete: Termwind::class, ); $this->singleton( - abstract: PrinterContract::class, - concrete: $this->config->printer() ?? DefaultPrinter::class, + abstract: Plates::class, + concrete: fn () => new Plates( + path: $this->config->views(), + ), ); $this->singleton( - abstract: EngineContract::class, - concrete: $this->config->engine() ?? DefaultEngine::class, + abstract: ThemeContract::class, + concrete: $this->config->theme() ?? DefaultTheme::class, ); $this->singleton( - abstract: Termwind::class, - concrete: Termwind::class, + abstract: PrinterContract::class, + concrete: $this->config->printer() ?? DefaultPrinter::class, ); $this->singleton( - abstract: Plates::class, - concrete: Plates::class, + abstract: EngineContract::class, + concrete: $this->config->engine() ?? DefaultEngine::class, ); } diff --git a/src/Output/Engine/TermwindEngine.php b/src/Output/Engine/TermwindEngine.php new file mode 100644 index 0000000..e75aa39 --- /dev/null +++ b/src/Output/Engine/TermwindEngine.php @@ -0,0 +1,198 @@ +termwind->render( + content: $message + ); + } + + public function lineBreak(): void + { + $this->termwind->render( + content: '
' + ); + } + + public function table(Table $table): void + { + $content = ''; + + foreach ($table->headers() as $header) { + $content .= ""; + } + + $content .= ''; + + foreach ($table->rows() as $row) { + $content .= ''; + + foreach ($row as $cell) { + $content .= ""; + } + + $content .= ''; + } + + $this->termwind->render( + content: "{$content}
{$header}
{$cell}
" + ); + } + + public function ask(string $question, string $method = 'default'): string + { + if ( ! method_exists($this->theme, $method)) { + throw new InvalidArgumentException("No output for [{$method}]"); + } + + /** @phpstan-ignore-next-line */ + return (string) $this->termwind->ask( + question: $this->formatMessage($question, $this->theme->{$method}()) + ); + } + + public function default(string $message): void + { + $this->termwind->render( + content: $this->formatMessage($message, $this->theme->default()) + ); + } + + public function alt(string $message): void + { + $this->termwind->render( + content: $this->formatMessage($message, $this->theme->alt()) + ); + } + + public function error(string $message): void + { + $this->termwind->render( + content: $this->formatMessage($message, $this->theme->error()) + ); + } + + public function errorAlt(string $message): void + { + $this->termwind->render( + content: $this->formatMessage($message, $this->theme->errorAlt()) + ); + } + + public function warning(string $message): void + { + $this->termwind->render( + content: $this->formatMessage($message, $this->theme->warning()) + ); + } + + public function warningAlt(string $message): void + { + $this->termwind->render( + content: $this->formatMessage($message, $this->theme->warningAlt()) + ); + } + + public function success(string $message): void + { + $this->termwind->render( + content: $this->formatMessage($message, $this->theme->success()) + ); + } + + public function successAlt(string $message): void + { + $this->termwind->render( + content: $this->formatMessage($message, $this->theme->successAlt()) + ); + } + + public function info(string $message): void + { + $this->termwind->render( + content: $this->formatMessage($message, $this->theme->info()) + ); + } + + public function infoAlt(string $message): void + { + $this->termwind->render( + content: $this->formatMessage($message, $this->theme->infoAlt()) + ); + } + + public function bold(string $message): void + { + $this->termwind->render( + content: $this->formatMessage($message, $this->theme->bold()) + ); + } + + public function dim(string $message): void + { + $this->termwind->render( + content: $this->formatMessage($message, $this->theme->dim()) + ); + } + + public function italic(string $message): void + { + $this->termwind->render( + content: $this->formatMessage($message, $this->theme->italic()) + ); + } + + public function underline(string $message): void + { + $this->termwind->render( + content: $this->formatMessage($message, $this->theme->underline()) + ); + } + + public function invert(string $message): void + { + $this->termwind->render( + content: $this->formatMessage($message, $this->theme->invert()) + ); + } + + private function formatMessage(string $message, ThemeStyle $style): string + { + $foreground = $style->foreground->value; + $background = $style->background?->value ?? ''; + + if ($background) { + $background = "bg-{$background}"; + } + + if ( ! in_array($foreground, TermwindStyle::fontStyles())) { + $foreground = "text-{$foreground}"; + } + + return << + {$message} + + HTML; + } +} diff --git a/src/Output/Table.php b/src/Output/Table.php index e6b0254..6eec348 100644 --- a/src/Output/Table.php +++ b/src/Output/Table.php @@ -18,6 +18,22 @@ public function __construct( ) { } + /** + * @return array + */ + public function headers(): array + { + return $this->headers; + } + + /** + * @return array> + */ + public function rows(): array + { + return $this->rows; + } + /** * @param array $row */ diff --git a/src/Theme/Enums/TermwindStyle.php b/src/Theme/Enums/TermwindStyle.php index 97d7ace..f6aa148 100644 --- a/src/Theme/Enums/TermwindStyle.php +++ b/src/Theme/Enums/TermwindStyle.php @@ -274,4 +274,17 @@ enum TermwindStyle: string implements MiniEnum case FONT_BOLD = 'font-bold'; case FONT_ITALLIC = 'italic'; case FONT_UNDERLINE = 'underline'; + + /** + * @return array + */ + public static function fontStyles(): array + { + return [ + self::FONT_NORMAL->value, + self::FONT_BOLD->value, + self::FONT_ITALLIC->value, + self::FONT_UNDERLINE->value, + ]; + } }